aurora-rendering-engine/examples/04_phase5_test/main.cpp

283 lines
8.6 KiB
C++

/**
* @file main.cpp
* @brief Phase 5 test: Hybrid GBuffer-driven CPU raytracing with primitive-id
*/
#include <are/core/logger.h>
#include <are/core/profiler.h>
#include <are/platform/window.h>
#include <are/platform/gl_context.h>
#include <are/rasterizer/rasterizer.h>
#include <are/rasterizer/gbuffer.h>
#include <are/rasterizer/shader_program.h>
#include <are/renderer/geometry_cache.h>
#include <are/scene/scene_manager.h>
#include <are/scene/camera.h>
#include <are/scene/mesh.h>
#include <are/scene/material.h>
#include <are/scene/directional_light.h>
#include <are/scene/point_light.h>
#include <are/raytracer/cpu_raytracer.h>
#include "../lib/glad/glad/glad.h"
#include <vector>
#include <string>
#include <memory>
#include <stdexcept>
using namespace are;
namespace {
uint32_t create_fullscreen_quad_vao() {
float vertices[] = {
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 1.0f
};
uint32_t indices[] = { 0, 1, 2, 2, 3, 0 };
uint32_t vao = 0, vbo = 0, ebo = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glBindVertexArray(0);
return vao;
}
uint32_t create_output_texture_rgba16f(int width, int height) {
uint32_t tex = 0;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
return tex;
}
} // namespace
int main() {
try {
Logger::init(LogLevel::ARE_LOG_INFO);
Profiler::init();
WindowConfig wc;
wc.width = 960;
wc.height = 540;
wc.title = "ARE Phase5 - Hybrid + PrimitiveID";
wc.vsync = true;
Window window(wc);
if (!GLContext::initialize()) {
ARE_LOG_CRITICAL("GLContext initialize failed");
return -1;
}
int fb_w = 0, fb_h = 0;
for (int i = 0; i < 240; ++i) {
window.poll_events();
window.get_framebuffer_size(fb_w, fb_h);
if (fb_w > 0 && fb_h > 0) break;
window.swap_buffers();
}
if (fb_w <= 0 || fb_h <= 0) {
ARE_LOG_CRITICAL("Invalid framebuffer size");
return -1;
}
Rasterizer rasterizer(fb_w, fb_h);
rasterizer.initialize_shaders("shaders/");
uint32_t output_tex = create_output_texture_rgba16f(fb_w, fb_h);
uint32_t quad_vao = create_fullscreen_quad_vao();
// Display shader (simple)
const char* vs = R"(
#version 430 core
layout(location = 0) in vec2 a_pos;
layout(location = 1) in vec2 a_uv;
out vec2 v_uv;
void main(){ v_uv=a_uv; gl_Position=vec4(a_pos,0,1); }
)";
const char* fs = R"(
#version 430 core
in vec2 v_uv;
out vec4 frag_color;
uniform sampler2D u_tex;
void main(){ frag_color = texture(u_tex, v_uv); }
)";
ShaderProgram display;
if (!display.compile_shader(ShaderType::ARE_SHADER_VERTEX, vs) ||
!display.compile_shader(ShaderType::ARE_SHADER_FRAGMENT, fs) ||
!display.link()) {
ARE_LOG_CRITICAL("Display shader compile failed");
return -1;
}
// Scene setup
SceneManager scene;
Material mat;
mat.set_albedo(Vec3(0.8f, 0.2f, 0.2f));
mat.set_roughness(0.6f);
MaterialHandle mat_h = scene.add_material(mat);
std::vector<Vertex> ground_v = {
Vertex(Vec3(-4, 0, -4), Vec3(0, 1, 0), Vec2(0, 0)),
Vertex(Vec3( 4, 0, -4), Vec3(0, 1, 0), Vec2(1, 0)),
Vertex(Vec3( 4, 0, 4), Vec3(0, 1, 0), Vec2(1, 1)),
Vertex(Vec3(-4, 0, 4), Vec3(0, 1, 0), Vec2(0, 1)),
};
std::vector<uint32_t> ground_i = { 0, 1, 2, 2, 3, 0 };
Mesh ground(ground_v, ground_i, mat_h);
ground.compute_tangents();
std::vector<Vertex> tri_v = {
Vertex(Vec3(-0.8f, 0.2f, 0.0f), Vec3(0, 1, 0), Vec2(0, 0)),
Vertex(Vec3( 0.8f, 0.2f, 0.0f), Vec3(0, 1, 0), Vec2(1, 0)),
Vertex(Vec3( 0.0f, 1.2f, 0.3f), Vec3(0, 1, 0), Vec2(0.5f, 1)),
};
std::vector<uint32_t> tri_i = { 0, 1, 2 };
Mesh tri_mesh(tri_v, tri_i, mat_h);
tri_mesh.compute_tangents();
// Upload meshes BEFORE adding (SceneManager stores copy)
rasterizer.upload_mesh(ground);
scene.add_mesh(ground);
rasterizer.upload_mesh(tri_mesh);
scene.add_mesh(tri_mesh);
auto sun = std::make_shared<DirectionalLight>(Vec3(-1, -1, -0.5f), Vec3(1.0f), 2.0f);
sun->set_cast_shadows(true);
scene.add_light(sun);
auto point = std::make_shared<PointLight>(Vec3(0, 2.5f, 1.5f), Vec3(1.0f, 0.95f, 0.8f), 10.0f, 10.0f);
point->set_cast_shadows(true);
scene.add_light(point);
Camera camera(Vec3(0.0f, 1.8f, 4.5f), Vec3(0.0f, 0.6f, 0.0f));
camera.set_perspective(45.0f, static_cast<Real>(fb_w) / static_cast<Real>(fb_h), 0.1f, 200.0f);
// Geometry cache: single source of truth
GeometryCache geom;
if (!geom.build_from_scene(scene)) {
ARE_LOG_CRITICAL("GeometryCache build failed");
return -1;
}
// Provide triangle base offsets to rasterizer for primitive id output
rasterizer.set_triangle_base_provider([&](size_t mesh_index) {
return geom.get_mesh_triangle_base(mesh_index);
});
// CPU ray tracer uses the same BVH (same triangle layout)
RayTracingConfig rtc;
rtc.backend = RayTracingBackend::ARE_RT_BACKEND_CPU;
rtc.spp = 1;
rtc.max_depth = 3;
rtc.enable_gi = false;
rtc.enable_ao = false;
rtc.ao_samples = 4;
rtc.ao_radius = 1.0f;
CPURayTracer tracer(rtc);
tracer.update_bvh(geom.get_bvh());
bool request_render = true;
while (!window.should_close()) {
window.poll_events();
if (window.is_key_pressed(256)) {
window.set_should_close(true);
}
int new_fb_w = 0, new_fb_h = 0;
window.get_framebuffer_size(new_fb_w, new_fb_h);
if (new_fb_w <= 0 || new_fb_h <= 0) {
window.swap_buffers();
continue;
}
if (new_fb_w != fb_w || new_fb_h != fb_h) {
fb_w = new_fb_w;
fb_h = new_fb_h;
rasterizer.resize(fb_w, fb_h);
glDeleteTextures(1, &output_tex);
output_tex = create_output_texture_rgba16f(fb_w, fb_h);
camera.set_aspect_ratio(static_cast<Real>(fb_w) / static_cast<Real>(fb_h));
request_render = true;
}
if (request_render) {
rasterizer.render_gbuffer(scene, camera);
tracer.render(scene, camera, &rasterizer.get_gbuffer(), output_tex);
request_render = false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, fb_w, fb_h);
glDisable(GL_DEPTH_TEST);
display.use();
display.set_uniform("u_tex", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, output_tex);
glBindVertexArray(quad_vao);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
window.swap_buffers();
}
glDeleteTextures(1, &output_tex);
glDeleteVertexArrays(1, &quad_vao);
Profiler::shutdown();
Logger::shutdown();
return 0;
} catch (const std::exception& e) {
Logger::init(LogLevel::ARE_LOG_INFO);
ARE_LOG_CRITICAL(std::string("Unhandled exception: ") + e.what());
Logger::shutdown();
return -1;
}
}