这个架构最后一次commit
parent
01dd5bd91e
commit
96ffcd4edc
|
|
@ -1,373 +1,282 @@
|
|||
/**
|
||||
* @file main.cpp
|
||||
* @brief Phase 5 verification program - CPU ray tracing test (RGBA16F output)
|
||||
* @brief Phase 5 test: Hybrid GBuffer-driven CPU raytracing with primitive-id
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <are/core/config.h>
|
||||
#include <are/core/logger.h>
|
||||
#include <are/core/profiler.h>
|
||||
|
||||
#include <are/platform/gl_context.h>
|
||||
#include <are/platform/window.h>
|
||||
#include <are/platform/gl_context.h>
|
||||
|
||||
#include <are/rasterizer/gbuffer.h>
|
||||
#include <are/rasterizer/rasterizer.h>
|
||||
#include <are/rasterizer/gbuffer.h>
|
||||
#include <are/rasterizer/shader_program.h>
|
||||
|
||||
#include <are/scene/camera.h>
|
||||
#include <are/scene/directional_light.h>
|
||||
#include <are/scene/material.h>
|
||||
#include <are/scene/mesh.h>
|
||||
#include <are/scene/point_light.h>
|
||||
#include <are/scene/scene_manager.h>
|
||||
#include <are/renderer/geometry_cache.h>
|
||||
|
||||
#include <are/acceleration/bvh.h>
|
||||
#include <are/geometry/triangle.h>
|
||||
#include <are/geometry/vertex.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 <glm/glm.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace are;
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief Create a fullscreen quad VAO.
|
||||
* @return VAO handle
|
||||
*/
|
||||
uint32_t create_fullscreen_quad_vao() {
|
||||
float vertices[] = {
|
||||
// pos // uv
|
||||
-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
|
||||
};
|
||||
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 indices[] = { 0, 1, 2, 2, 3, 0 };
|
||||
uint32_t vao = 0, vbo = 0, ebo = 0;
|
||||
glGenVertexArrays(1, &vao);
|
||||
glBindVertexArray(vao);
|
||||
|
||||
uint32_t vao = 0;
|
||||
uint32_t vbo = 0;
|
||||
uint32_t ebo = 0;
|
||||
glGenBuffers(1, &vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
|
||||
glGenVertexArrays(1, &vao);
|
||||
glBindVertexArray(vao);
|
||||
glGenBuffers(1, &ebo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
|
||||
|
||||
glGenBuffers(1, &vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
|
||||
|
||||
glGenBuffers(1, &ebo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
|
||||
|
||||
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);
|
||||
|
||||
// Intentionally leak vbo/ebo for this small test (or store & delete if you prefer).
|
||||
return vao;
|
||||
glBindVertexArray(0);
|
||||
return vao;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create RGBA16F output texture.
|
||||
* @param width Texture width
|
||||
* @param height Texture height
|
||||
* @return Texture ID
|
||||
*/
|
||||
uint32_t create_output_texture_rgba16f(int width, int height) {
|
||||
uint32_t tex = 0;
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
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);
|
||||
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_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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build triangles from a mesh (positions only, identity transform).
|
||||
* @param mesh Mesh
|
||||
* @param material Material handle
|
||||
* @return Triangle list
|
||||
*/
|
||||
std::vector<Triangle> mesh_to_triangles(const Mesh &mesh, MaterialHandle material) {
|
||||
std::vector<Triangle> tris;
|
||||
if (mesh.is_empty()) {
|
||||
return tris;
|
||||
}
|
||||
|
||||
const auto &v = mesh.get_vertices();
|
||||
const auto &idx = mesh.get_indices();
|
||||
|
||||
if (idx.size() % 3 != 0) {
|
||||
ARE_LOG_ERROR("phase5_test: mesh indices not multiple of 3");
|
||||
return tris;
|
||||
}
|
||||
|
||||
tris.reserve(idx.size() / 3);
|
||||
for (size_t i = 0; i < idx.size(); i += 3) {
|
||||
uint32_t i0 = idx[i + 0];
|
||||
uint32_t i1 = idx[i + 1];
|
||||
uint32_t i2 = idx[i + 2];
|
||||
if (i0 >= v.size() || i1 >= v.size() || i2 >= v.size()) {
|
||||
continue;
|
||||
}
|
||||
Triangle t(v[i0], v[i1], v[i2], material);
|
||||
tris.push_back(t);
|
||||
}
|
||||
return tris;
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return tex;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
Logger::init(LogLevel::ARE_LOG_INFO);
|
||||
Profiler::init();
|
||||
try {
|
||||
Logger::init(LogLevel::ARE_LOG_INFO);
|
||||
Profiler::init();
|
||||
|
||||
WindowConfig wc;
|
||||
wc.width = 960;
|
||||
wc.height = 540;
|
||||
wc.title = "ARE Phase 5 - CPU Ray Tracing (RGBA16F)";
|
||||
wc.vsync = true;
|
||||
WindowConfig wc;
|
||||
wc.width = 960;
|
||||
wc.height = 540;
|
||||
wc.title = "ARE Phase5 - Hybrid + PrimitiveID";
|
||||
wc.vsync = true;
|
||||
|
||||
Window window(wc);
|
||||
Window window(wc);
|
||||
|
||||
if (!GLContext::initialize()) {
|
||||
ARE_LOG_CRITICAL("phase5_test: GLContext::initialize failed");
|
||||
return -1;
|
||||
}
|
||||
GLContext::print_info();
|
||||
if (!GLContext::initialize()) {
|
||||
ARE_LOG_CRITICAL("GLContext initialize failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fb_w = 0;
|
||||
int fb_h = 0;
|
||||
window.get_framebuffer_size(fb_w, fb_h);
|
||||
if (fb_w <= 0 || fb_h <= 0) {
|
||||
ARE_LOG_CRITICAL("phase5_test: framebuffer size is invalid");
|
||||
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;
|
||||
}
|
||||
|
||||
// GBuffer only used for resolution / future hybrid path
|
||||
Rasterizer rasterizer(fb_w, fb_h);
|
||||
GBuffer &gbuffer = rasterizer.get_gbuffer();
|
||||
Rasterizer rasterizer(fb_w, fb_h);
|
||||
rasterizer.initialize_shaders("shaders/");
|
||||
|
||||
// Output texture (RGBA16F)
|
||||
uint32_t output_tex = create_output_texture_rgba16f(fb_w, fb_h);
|
||||
uint32_t output_tex = create_output_texture_rgba16f(fb_w, fb_h);
|
||||
uint32_t quad_vao = create_fullscreen_quad_vao();
|
||||
|
||||
// Simple display shader
|
||||
const char *fsq_vs = R"(
|
||||
// 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.0, 1.0);
|
||||
}
|
||||
void main(){ v_uv=a_uv; gl_Position=vec4(a_pos,0,1); }
|
||||
)";
|
||||
|
||||
const char *fsq_fs = R"(
|
||||
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);
|
||||
}
|
||||
void main(){ frag_color = texture(u_tex, v_uv); }
|
||||
)";
|
||||
|
||||
ShaderProgram display;
|
||||
if (!display.compile_shader(ShaderType::ARE_SHADER_VERTEX, fsq_vs) || !display.compile_shader(ShaderType::ARE_SHADER_FRAGMENT, fsq_fs) || !display.link()) {
|
||||
ARE_LOG_CRITICAL("phase5_test: failed to compile display shader");
|
||||
return -1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
uint32_t quad_vao = create_fullscreen_quad_vao();
|
||||
// Scene setup
|
||||
SceneManager scene;
|
||||
|
||||
// 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);
|
||||
|
||||
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();
|
||||
|
||||
// A ground plane (two triangles)
|
||||
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();
|
||||
MeshHandle ground_mh = scene.add_mesh(ground);
|
||||
(void)ground_mh;
|
||||
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();
|
||||
|
||||
// A tilted triangle
|
||||
std::vector<Vertex> tri_v = {
|
||||
Vertex(Vec3(-0.8f, 0.2f, 0.0f), Vec3(0, 0.8f, 0.6f), Vec2(0, 0)),
|
||||
Vertex(Vec3(0.8f, 0.2f, 0.0f), Vec3(0, 0.8f, 0.6f), Vec2(1, 0)),
|
||||
Vertex(Vec3(0.0f, 1.2f, 0.3f), Vec3(0, 0.8f, 0.6f), 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();
|
||||
scene.add_mesh(tri_mesh);
|
||||
// Upload meshes BEFORE adding (SceneManager stores copy)
|
||||
rasterizer.upload_mesh(ground);
|
||||
scene.add_mesh(ground);
|
||||
|
||||
// Lights
|
||||
auto sun = std::make_shared<DirectionalLight>(Vec3(-1, -1, -0.5f), Vec3(1.0f), 2.0f);
|
||||
scene.add_light(sun);
|
||||
rasterizer.upload_mesh(tri_mesh);
|
||||
scene.add_mesh(tri_mesh);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
// Camera
|
||||
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);
|
||||
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);
|
||||
|
||||
// Build BVH from scene meshes
|
||||
std::vector<Triangle> triangles;
|
||||
for (const auto &m : scene.get_all_meshes()) {
|
||||
auto tris = mesh_to_triangles(m, m.get_material());
|
||||
triangles.insert(triangles.end(), tris.begin(), tris.end());
|
||||
}
|
||||
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);
|
||||
|
||||
BVH bvh;
|
||||
if (!bvh.build(triangles)) {
|
||||
ARE_LOG_CRITICAL("phase5_test: BVH build failed");
|
||||
return -1;
|
||||
}
|
||||
// Geometry cache: single source of truth
|
||||
GeometryCache geom;
|
||||
if (!geom.build_from_scene(scene)) {
|
||||
ARE_LOG_CRITICAL("GeometryCache build failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Ray tracer config
|
||||
RayTracingConfig rtc;
|
||||
rtc.backend = RayTracingBackend::ARE_RT_BACKEND_CPU;
|
||||
rtc.spp = 1;
|
||||
rtc.max_depth = 4;
|
||||
rtc.enable_gi = false;
|
||||
rtc.enable_ao = false;
|
||||
rtc.ao_samples = 1;
|
||||
rtc.ao_radius = 1.0f;
|
||||
// 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);
|
||||
});
|
||||
|
||||
CPURayTracer tracer(rtc);
|
||||
tracer.update_bvh(bvh);
|
||||
// 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;
|
||||
|
||||
ARE_LOG_INFO("Controls:");
|
||||
ARE_LOG_INFO(" 1: spp = 1");
|
||||
ARE_LOG_INFO(" 2: spp = 16");
|
||||
ARE_LOG_INFO(" A: toggle AO");
|
||||
ARE_LOG_INFO(" G: toggle GI");
|
||||
ARE_LOG_INFO(" ESC: exit");
|
||||
CPURayTracer tracer(rtc);
|
||||
tracer.update_bvh(geom.get_bvh());
|
||||
|
||||
bool request_render = true;
|
||||
bool request_render = true;
|
||||
|
||||
while (!window.should_close()) {
|
||||
std::cout << "RENDERING" << std::endl;
|
||||
window.poll_events();
|
||||
while (!window.should_close()) {
|
||||
window.poll_events();
|
||||
|
||||
if (window.is_key_pressed(256)) {
|
||||
window.set_should_close(true);
|
||||
}
|
||||
if (window.is_key_pressed(256)) {
|
||||
window.set_should_close(true);
|
||||
}
|
||||
|
||||
if (window.is_key_pressed(49)) { // 1
|
||||
rtc.spp = 1;
|
||||
tracer.set_config(rtc);
|
||||
request_render = true;
|
||||
}
|
||||
if (window.is_key_pressed(50)) { // 2
|
||||
rtc.spp = 16;
|
||||
tracer.set_config(rtc);
|
||||
request_render = true;
|
||||
}
|
||||
if (window.is_key_pressed(65)) { // A
|
||||
rtc.enable_ao = !rtc.enable_ao;
|
||||
tracer.set_config(rtc);
|
||||
request_render = true;
|
||||
}
|
||||
if (window.is_key_pressed(71)) { // G
|
||||
rtc.enable_gi = !rtc.enable_gi;
|
||||
tracer.set_config(rtc);
|
||||
request_render = 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;
|
||||
}
|
||||
|
||||
// Handle resize
|
||||
int new_fb_w = 0;
|
||||
int new_fb_h = 0;
|
||||
window.get_framebuffer_size(new_fb_w, new_fb_h);
|
||||
if (new_fb_w != fb_w || new_fb_h != fb_h) {
|
||||
fb_w = new_fb_w;
|
||||
fb_h = new_fb_h;
|
||||
|
||||
// If minimized / invalid size: keep window responsive, skip rendering
|
||||
if (new_fb_w <= 0 || new_fb_h <= 0) {
|
||||
window.swap_buffers();
|
||||
continue;
|
||||
}
|
||||
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));
|
||||
|
||||
if (new_fb_w != fb_w || new_fb_h != fb_h) {
|
||||
fb_w = new_fb_w;
|
||||
fb_h = new_fb_h;
|
||||
request_render = true;
|
||||
}
|
||||
|
||||
rasterizer.resize(fb_w, fb_h);
|
||||
if (request_render) {
|
||||
rasterizer.render_gbuffer(scene, camera);
|
||||
tracer.render(scene, camera, &rasterizer.get_gbuffer(), output_tex);
|
||||
request_render = false;
|
||||
}
|
||||
|
||||
glDeleteTextures(1, &output_tex);
|
||||
output_tex = create_output_texture_rgba16f(fb_w, fb_h);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glViewport(0, 0, fb_w, fb_h);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
camera.set_aspect_ratio(static_cast<Real>(fb_w) / static_cast<Real>(fb_h));
|
||||
display.use();
|
||||
display.set_uniform("u_tex", 0);
|
||||
|
||||
request_render = true;
|
||||
}
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, output_tex);
|
||||
|
||||
if (request_render) {
|
||||
ARE_PROFILE_SCOPE("CPU Ray Trace");
|
||||
tracer.render(scene, camera, &gbuffer, output_tex);
|
||||
request_render = false;
|
||||
}
|
||||
glBindVertexArray(quad_vao);
|
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
|
||||
glBindVertexArray(0);
|
||||
|
||||
// Present
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glViewport(0, 0, fb_w, fb_h);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
window.swap_buffers();
|
||||
}
|
||||
|
||||
display.use();
|
||||
display.set_uniform("u_tex", 0);
|
||||
glDeleteTextures(1, &output_tex);
|
||||
glDeleteVertexArrays(1, &quad_vao);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, output_tex);
|
||||
Profiler::shutdown();
|
||||
Logger::shutdown();
|
||||
return 0;
|
||||
|
||||
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::print_results();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@
|
|||
#ifndef ARE_INCLUDE_ACCELERATION_BVH_H
|
||||
#define ARE_INCLUDE_ACCELERATION_BVH_H
|
||||
|
||||
#include <are/core/types.h>
|
||||
#include <are/acceleration/bvh_node.h>
|
||||
#include <are/acceleration/bvh_builder.h>
|
||||
#include <are/acceleration/bvh_node.h>
|
||||
#include <are/core/types.h>
|
||||
#include <are/geometry/triangle.h>
|
||||
#include <are/raytracer/ray.h>
|
||||
#include <are/raytracer/hit_record.h>
|
||||
#include <are/raytracer/ray.h>
|
||||
#include <vector>
|
||||
|
||||
namespace are {
|
||||
|
|
@ -22,98 +22,112 @@ namespace are {
|
|||
*/
|
||||
class BVH {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
BVH();
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
BVH();
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~BVH();
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~BVH();
|
||||
|
||||
/**
|
||||
* @brief Build BVH from triangle list
|
||||
* @param triangles Triangle list
|
||||
* @param config Build configuration
|
||||
* @return true if build succeeded
|
||||
*/
|
||||
bool build(const std::vector<Triangle>& triangles,
|
||||
const BVHBuildConfig& config = BVHBuildConfig());
|
||||
/**
|
||||
* @brief Build BVH from triangle list
|
||||
* @param triangles Triangle list
|
||||
* @param config Build configuration
|
||||
* @return true if build succeeded
|
||||
*/
|
||||
bool build(const std::vector<Triangle> &triangles,
|
||||
const BVHBuildConfig &config = BVHBuildConfig());
|
||||
|
||||
/**
|
||||
* @brief Traverse BVH and find closest intersection
|
||||
* @param ray Ray to trace
|
||||
* @param hit Output hit record
|
||||
* @return true if intersection found
|
||||
*/
|
||||
bool intersect(const Ray& ray, HitRecord& hit) const;
|
||||
/**
|
||||
* @brief Traverse BVH and find closest intersection
|
||||
* @param ray Ray to trace
|
||||
* @param hit Output hit record
|
||||
* @return true if intersection found
|
||||
*/
|
||||
bool intersect(const Ray &ray, HitRecord &hit) const;
|
||||
|
||||
/**
|
||||
* @brief Fast occlusion test (any hit)
|
||||
* @param ray Ray to trace
|
||||
* @param t_max Maximum t value
|
||||
* @return true if any intersection found
|
||||
*/
|
||||
bool intersect_any(const Ray& ray, Real t_max) const;
|
||||
/**
|
||||
* @brief Fast occlusion test (any hit)
|
||||
* @param ray Ray to trace
|
||||
* @param t_max Maximum t value
|
||||
* @return true if any intersection found
|
||||
*/
|
||||
bool intersect_any(const Ray &ray, Real t_max) const;
|
||||
/**
|
||||
* @brief Fast occlusion test (any hit), with ignored triangle id
|
||||
* @param ray Ray to trace
|
||||
* @param t_max Maximum t value
|
||||
* @param ignore_triangle_index Triangle index to ignore (e.g. self primitive id)
|
||||
* @return true if any intersection found (excluding ignored triangle)
|
||||
*/
|
||||
bool intersect_any(const Ray &ray, Real t_max, uint32_t ignore_triangle_index) const;
|
||||
|
||||
/**
|
||||
* @brief Check if BVH is built
|
||||
* @return true if built
|
||||
*/
|
||||
bool is_built() const { return !nodes_.empty(); }
|
||||
/**
|
||||
* @brief Check if BVH is built
|
||||
* @return true if built
|
||||
*/
|
||||
bool is_built() const {
|
||||
return !nodes_.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get BVH nodes (for GPU upload)
|
||||
* @return Node array
|
||||
*/
|
||||
const std::vector<BVHNode>& get_nodes() const { return nodes_; }
|
||||
/**
|
||||
* @brief Get BVH nodes (for GPU upload)
|
||||
* @return Node array
|
||||
*/
|
||||
const std::vector<BVHNode> &get_nodes() const {
|
||||
return nodes_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get primitive indices
|
||||
* @return Index array
|
||||
*/
|
||||
const std::vector<uint32_t>& get_primitive_indices() const {
|
||||
return primitive_indices_;
|
||||
}
|
||||
/**
|
||||
* @brief Get primitive indices
|
||||
* @return Index array
|
||||
*/
|
||||
const std::vector<uint32_t> &get_primitive_indices() const {
|
||||
return primitive_indices_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get triangles
|
||||
* @return Triangle array
|
||||
*/
|
||||
const std::vector<Triangle>& get_triangles() const { return triangles_; }
|
||||
/**
|
||||
* @brief Get triangles
|
||||
* @return Triangle array
|
||||
*/
|
||||
const std::vector<Triangle> &get_triangles() const {
|
||||
return triangles_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get memory usage in bytes
|
||||
* @return Memory usage
|
||||
*/
|
||||
size_t get_memory_usage() const;
|
||||
/**
|
||||
* @brief Get memory usage in bytes
|
||||
* @return Memory usage
|
||||
*/
|
||||
size_t get_memory_usage() const;
|
||||
|
||||
/**
|
||||
* @brief Clear BVH data
|
||||
*/
|
||||
void clear();
|
||||
/**
|
||||
* @brief Clear BVH data
|
||||
*/
|
||||
void clear();
|
||||
|
||||
private:
|
||||
// Recursive traversal (kept for reference)
|
||||
bool intersect_recursive(uint32_t node_index, const Ray& ray, HitRecord& hit) const;
|
||||
bool intersect_any_recursive(uint32_t node_index, const Ray& ray, Real t_max) const;
|
||||
// Recursive traversal (kept for reference)
|
||||
bool intersect_recursive(uint32_t node_index, const Ray &ray, HitRecord &hit) const;
|
||||
bool intersect_any_recursive(uint32_t node_index, const Ray &ray, Real t_max) const;
|
||||
|
||||
// Optimized iterative traversal
|
||||
bool intersect_iterative(const Ray& ray, HitRecord& hit) const;
|
||||
bool intersect_any_iterative(const Ray& ray, Real t_max) const;
|
||||
// Optimized iterative traversal
|
||||
bool intersect_iterative(const Ray &ray, HitRecord &hit) const;
|
||||
bool intersect_any_iterative(const Ray &ray, Real t_max) const;
|
||||
|
||||
// Fast intersection helpers
|
||||
inline bool intersect_aabb_fast(const AABB& bounds, const Ray& ray,
|
||||
const Vec3& inv_dir, Real t_max,
|
||||
Real& t_min_out, Real& t_max_out) const;
|
||||
inline bool intersect_triangle_fast(const Triangle& triangle, const Ray& ray,
|
||||
Real t_max, HitRecord& hit) const;
|
||||
// Fast intersection helpers
|
||||
inline bool intersect_aabb_fast(const AABB &bounds, const Ray &ray,
|
||||
const Vec3 &inv_dir, Real t_max,
|
||||
Real &t_min_out, Real &t_max_out) const;
|
||||
inline bool intersect_triangle_fast(const Triangle &triangle, const Ray &ray,
|
||||
Real t_max, HitRecord &hit) const;
|
||||
|
||||
std::vector<BVHNode> nodes_; ///< BVH nodes
|
||||
std::vector<uint32_t> primitive_indices_; ///< Primitive index array
|
||||
std::vector<Triangle> triangles_; ///< Triangle data
|
||||
uint32_t root_index_; ///< Root node index
|
||||
std::vector<BVHNode> nodes_; ///< BVH nodes
|
||||
std::vector<uint32_t> primitive_indices_; ///< Primitive index array
|
||||
std::vector<Triangle> triangles_; ///< Triangle data
|
||||
uint32_t root_index_; ///< Root node index
|
||||
};
|
||||
|
||||
} // namespace are
|
||||
|
|
|
|||
|
|
@ -15,7 +15,13 @@ namespace are {
|
|||
* @class GBuffer
|
||||
* @brief G-Buffer for deferred rendering
|
||||
*
|
||||
* Contains multiple render targets for position, normal, albedo, etc.
|
||||
* Attachment layout:
|
||||
* 0: position (RGB16F)
|
||||
* 1: normal (RGB16F)
|
||||
* 2: albedo+metallic (RGBA8)
|
||||
* 3: roughness+ao (RG8)
|
||||
* 4: primitive id (R32UI)
|
||||
* Depth: depth texture (DEPTH_COMPONENT24)
|
||||
*/
|
||||
class GBuffer {
|
||||
public:
|
||||
|
|
@ -55,7 +61,7 @@ public:
|
|||
|
||||
/**
|
||||
* @brief Bind texture for reading
|
||||
* @param index Texture index (0=position, 1=normal, 2=albedo, etc.)
|
||||
* @param index Texture index
|
||||
* @param texture_unit Texture unit to bind to
|
||||
*/
|
||||
void bind_texture(int index, int texture_unit);
|
||||
|
|
@ -66,6 +72,7 @@ public:
|
|||
uint32_t get_albedo_texture() const { return albedo_texture_; }
|
||||
uint32_t get_material_texture() const { return material_texture_; }
|
||||
uint32_t get_depth_texture() const { return depth_texture_; }
|
||||
uint32_t get_primitive_id_texture() const { return primitive_id_texture_; }
|
||||
|
||||
// Dimensions
|
||||
int get_width() const { return width_; }
|
||||
|
|
@ -73,6 +80,15 @@ public:
|
|||
|
||||
/**
|
||||
* @brief Read pixel data from G-Buffer
|
||||
*
|
||||
* Index mapping:
|
||||
* - 0: position (RGB16F) -> GL_RGB/GL_FLOAT
|
||||
* - 1: normal (RGB16F) -> GL_RGB/GL_FLOAT
|
||||
* - 2: albedo_metallic (RGBA8) -> GL_RGBA/GL_UNSIGNED_BYTE
|
||||
* - 3: material (RG8) -> GL_RG/GL_UNSIGNED_BYTE
|
||||
* - 4: depth (DEPTH_COMPONENT24) -> GL_DEPTH_COMPONENT/GL_FLOAT
|
||||
* - 5: primitive id (R32UI) -> GL_RED_INTEGER/GL_UNSIGNED_INT
|
||||
*
|
||||
* @param index Buffer index
|
||||
* @param data Output data pointer
|
||||
*/
|
||||
|
|
@ -84,17 +100,17 @@ private:
|
|||
void create_framebuffer();
|
||||
|
||||
uint32_t fbo_; ///< Framebuffer object
|
||||
uint32_t rbo_depth_; ///< Depth renderbuffer
|
||||
uint32_t rbo_depth_; ///< Legacy depth renderbuffer (unused)
|
||||
|
||||
// G-Buffer textures
|
||||
uint32_t position_texture_; ///< World position (RGB16F)
|
||||
uint32_t normal_texture_; ///< World normal (RGB16F)
|
||||
uint32_t albedo_texture_; ///< Albedo + Metallic (RGBA8)
|
||||
uint32_t material_texture_; ///< Roughness + AO (RG8)
|
||||
uint32_t depth_texture_; ///< Depth (R32F)
|
||||
uint32_t position_texture_;
|
||||
uint32_t normal_texture_;
|
||||
uint32_t albedo_texture_;
|
||||
uint32_t material_texture_;
|
||||
uint32_t depth_texture_;
|
||||
uint32_t primitive_id_texture_;
|
||||
|
||||
int width_; ///< Buffer width
|
||||
int height_; ///< Buffer height
|
||||
int width_;
|
||||
int height_;
|
||||
};
|
||||
|
||||
} // namespace are
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@
|
|||
#define ARE_INCLUDE_RASTERIZER_RASTERIZER_H
|
||||
|
||||
#include <are/core/types.h>
|
||||
#include <are/core/config.h>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace are {
|
||||
|
||||
// Forward declarations
|
||||
class GBuffer;
|
||||
class ShaderProgram;
|
||||
class SceneManager;
|
||||
|
|
@ -20,67 +20,52 @@ class Camera;
|
|||
class Mesh;
|
||||
|
||||
/**
|
||||
* @class Rasterizer
|
||||
* @brief OpenGL rasterization pipeline
|
||||
*
|
||||
* Renders scene geometry to G-Buffer using traditional rasterization.
|
||||
* @struct RasterizerState
|
||||
* @brief Rasterizer fixed-function state (configurable)
|
||||
*/
|
||||
struct RasterizerState {
|
||||
bool enable_depth_test = true;
|
||||
bool enable_cull_face = false;
|
||||
uint32_t cull_face_mode = 0x0405; // GL_BACK
|
||||
uint32_t front_face = 0x0901; // GL_CCW
|
||||
};
|
||||
|
||||
class Rasterizer {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param width Framebuffer width
|
||||
* @param height Framebuffer height
|
||||
*/
|
||||
Rasterizer(int width, int height);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~Rasterizer();
|
||||
|
||||
/**
|
||||
* @brief Resize framebuffer
|
||||
* @param width New width
|
||||
* @param height New height
|
||||
*/
|
||||
void resize(int width, int height);
|
||||
|
||||
/**
|
||||
* @brief Render scene to G-Buffer
|
||||
* @param scene Scene manager
|
||||
* @param camera Camera
|
||||
*/
|
||||
void render_gbuffer(const SceneManager& scene, const Camera& camera);
|
||||
|
||||
/**
|
||||
* @brief Get G-Buffer
|
||||
* @return G-Buffer reference
|
||||
*/
|
||||
GBuffer& get_gbuffer();
|
||||
const GBuffer& get_gbuffer() const;
|
||||
|
||||
/**
|
||||
* @brief Upload mesh data to GPU
|
||||
* @param mesh Mesh to upload
|
||||
*/
|
||||
void upload_mesh(Mesh& mesh);
|
||||
|
||||
/**
|
||||
* @brief Delete mesh GPU resources
|
||||
* @param mesh Mesh to delete
|
||||
*/
|
||||
void delete_mesh(Mesh& mesh);
|
||||
|
||||
private:
|
||||
void initialize_shaders(const std::string& shader_dir);
|
||||
|
||||
void set_triangle_base_provider(std::function<uint32_t(size_t)> provider);
|
||||
|
||||
/**
|
||||
* @brief Set rasterizer fixed-function state
|
||||
* @param state State
|
||||
*/
|
||||
void set_state(const RasterizerState& state);
|
||||
|
||||
private:
|
||||
void setup_mesh_buffers(Mesh& mesh);
|
||||
|
||||
std::unique_ptr<GBuffer> gbuffer_; ///< G-Buffer
|
||||
std::unique_ptr<ShaderProgram> gbuffer_shader_; ///< G-Buffer shader
|
||||
std::unique_ptr<GBuffer> gbuffer_;
|
||||
std::unique_ptr<ShaderProgram> gbuffer_shader_;
|
||||
|
||||
int width_; ///< Framebuffer width
|
||||
int height_; ///< Framebuffer height
|
||||
std::function<uint32_t(size_t)> triangle_base_provider_;
|
||||
RasterizerState state_;
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
};
|
||||
|
||||
} // namespace are
|
||||
|
|
|
|||
|
|
@ -12,73 +12,28 @@
|
|||
|
||||
namespace are {
|
||||
|
||||
/**
|
||||
* @enum ShaderType
|
||||
* @brief Shader stage types
|
||||
*/
|
||||
enum class ShaderType {
|
||||
ARE_SHADER_VERTEX,
|
||||
ARE_SHADER_FRAGMENT,
|
||||
ARE_SHADER_COMPUTE
|
||||
};
|
||||
|
||||
/**
|
||||
* @class ShaderProgram
|
||||
* @brief OpenGL shader program management
|
||||
*/
|
||||
class ShaderProgram {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
ShaderProgram();
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~ShaderProgram();
|
||||
|
||||
/**
|
||||
* @brief Load and compile shader from file
|
||||
* @param type Shader type
|
||||
* @param filepath Shader file path
|
||||
* @return true if compilation succeeded
|
||||
*/
|
||||
bool load_shader(ShaderType type, const std::string& filepath);
|
||||
|
||||
/**
|
||||
* @brief Compile shader from source string
|
||||
* @param type Shader type
|
||||
* @param source Shader source code
|
||||
* @return true if compilation succeeded
|
||||
*/
|
||||
bool compile_shader(ShaderType type, const std::string& source);
|
||||
|
||||
/**
|
||||
* @brief Link shader program
|
||||
* @return true if linking succeeded
|
||||
*/
|
||||
bool link();
|
||||
|
||||
/**
|
||||
* @brief Use this shader program
|
||||
*/
|
||||
void use() const;
|
||||
|
||||
/**
|
||||
* @brief Check if program is valid
|
||||
* @return true if valid
|
||||
*/
|
||||
bool is_valid() const { return program_ != 0 && linked_; }
|
||||
|
||||
/**
|
||||
* @brief Get OpenGL program ID
|
||||
* @return Program ID
|
||||
*/
|
||||
uint32_t get_program() const { return program_; }
|
||||
|
||||
// Uniform setters
|
||||
void set_uniform(const std::string& name, int value);
|
||||
void set_uniform(const std::string& name, uint32_t value); ///< NEW
|
||||
void set_uniform(const std::string& name, float value);
|
||||
void set_uniform(const std::string& name, const Vec2& value);
|
||||
void set_uniform(const std::string& name, const Vec3& value);
|
||||
|
|
@ -86,24 +41,18 @@ public:
|
|||
void set_uniform(const std::string& name, const Mat3& value);
|
||||
void set_uniform(const std::string& name, const Mat4& value);
|
||||
|
||||
/**
|
||||
* @brief Get uniform location (cached)
|
||||
* @param name Uniform name
|
||||
* @return Uniform location (-1 if not found)
|
||||
*/
|
||||
int get_uniform_location(const std::string& name);
|
||||
|
||||
private:
|
||||
bool check_compile_errors(uint32_t shader, ShaderType type);
|
||||
bool check_link_errors();
|
||||
|
||||
uint32_t program_; ///< OpenGL program ID
|
||||
uint32_t vertex_shader_; ///< Vertex shader ID
|
||||
uint32_t fragment_shader_; ///< Fragment shader ID
|
||||
uint32_t compute_shader_; ///< Compute shader ID
|
||||
|
||||
bool linked_; ///< Link status
|
||||
std::unordered_map<std::string, int> uniform_cache_; ///< Uniform location cache
|
||||
uint32_t program_;
|
||||
uint32_t vertex_shader_;
|
||||
uint32_t fragment_shader_;
|
||||
uint32_t compute_shader_;
|
||||
bool linked_;
|
||||
std::unordered_map<std::string, int> uniform_cache_;
|
||||
};
|
||||
|
||||
} // namespace are
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ private:
|
|||
* @param max_distance Maximum distance
|
||||
* @return true if in shadow
|
||||
*/
|
||||
bool is_in_shadow(const Vec3& origin, const Vec3& direction, Real max_distance);
|
||||
bool is_in_shadow(const Vec3& origin, const Vec3& direction, Real max_distance, uint32_t ignore_triangle);
|
||||
|
||||
const BVH* bvh_; ///< BVH reference
|
||||
const SceneManager* scene_; ///< Scene reference
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* @file geometry_cache.h
|
||||
* @brief Frame-level geometry cache for consistent BVH and GBuffer primitive ids
|
||||
*/
|
||||
|
||||
#ifndef ARE_INCLUDE_RENDERER_GEOMETRY_CACHE_H
|
||||
#define ARE_INCLUDE_RENDERER_GEOMETRY_CACHE_H
|
||||
|
||||
#include <are/core/types.h>
|
||||
#include <are/geometry/triangle.h>
|
||||
#include <are/acceleration/bvh.h>
|
||||
#include <are/scene/scene_manager.h>
|
||||
#include <vector>
|
||||
|
||||
namespace are {
|
||||
|
||||
/**
|
||||
* @class GeometryCache
|
||||
* @brief Builds a global triangle list and BVH from a SceneManager snapshot.
|
||||
*
|
||||
* Provides a single source of truth for:
|
||||
* - Global triangle array layout (used by BVH and RayTracer)
|
||||
* - Mesh -> triangle base mapping (used by Rasterizer for primitive id output)
|
||||
*/
|
||||
class GeometryCache {
|
||||
public:
|
||||
/**
|
||||
* @brief Build cache from scene
|
||||
* @param scene Scene manager
|
||||
* @param bvh_config BVH build config
|
||||
* @return true if succeeded
|
||||
*/
|
||||
bool build_from_scene(const SceneManager& scene,
|
||||
const BVHBuildConfig& bvh_config = BVHBuildConfig());
|
||||
|
||||
/**
|
||||
* @brief Get BVH reference
|
||||
* @return BVH
|
||||
*/
|
||||
const BVH& get_bvh() const { return bvh_; }
|
||||
|
||||
/**
|
||||
* @brief Get global triangles
|
||||
* @return Triangle array
|
||||
*/
|
||||
const std::vector<Triangle>& get_triangles() const { return triangles_; }
|
||||
|
||||
/**
|
||||
* @brief Get triangle base for mesh by index into scene.get_all_meshes()
|
||||
* @param mesh_index Mesh index in scene.get_all_meshes()
|
||||
* @return Base triangle id
|
||||
*/
|
||||
uint32_t get_mesh_triangle_base(size_t mesh_index) const;
|
||||
|
||||
private:
|
||||
std::vector<Triangle> triangles_;
|
||||
std::vector<uint32_t> mesh_triangle_base_;
|
||||
BVH bvh_;
|
||||
};
|
||||
|
||||
} // namespace are
|
||||
|
||||
#endif // ARE_INCLUDE_RENDERER_GEOMETRY_CACHE_H
|
||||
|
|
@ -1,33 +1,26 @@
|
|||
#version 430 core
|
||||
|
||||
// Inputs from vertex shader
|
||||
in vec3 v_world_position;
|
||||
in vec3 v_world_normal;
|
||||
in vec2 v_texcoord;
|
||||
in vec3 v_world_tangent;
|
||||
flat in uint v_triangle_id_base;
|
||||
|
||||
// Material uniforms
|
||||
uniform vec3 u_albedo;
|
||||
uniform float u_metallic;
|
||||
uniform float u_roughness;
|
||||
|
||||
// G-Buffer outputs
|
||||
layout(location = 0) out vec3 g_position;
|
||||
layout(location = 1) out vec3 g_normal;
|
||||
layout(location = 2) out vec4 g_albedo_metallic;
|
||||
layout(location = 3) out vec2 g_roughness_ao;
|
||||
layout(location = 4) out uint g_primitive_id;
|
||||
|
||||
void main() {
|
||||
// Output world position
|
||||
g_position = v_world_position;
|
||||
|
||||
// Output normalized world normal
|
||||
g_normal = normalize(v_world_normal);
|
||||
|
||||
// Output albedo (RGB) and metallic (A)
|
||||
g_albedo_metallic = vec4(u_albedo, u_metallic);
|
||||
|
||||
// Output roughness (R) and ambient occlusion (G)
|
||||
// AO is set to 1.0 by default (no occlusion)
|
||||
g_roughness_ao = vec2(u_roughness, 1.0);
|
||||
|
||||
// Global primitive ID = mesh base + primitive id within draw call
|
||||
g_primitive_id = v_triangle_id_base + uint(gl_PrimitiveID);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,29 @@
|
|||
#version 430 core
|
||||
|
||||
// Vertex attributes
|
||||
layout(location = 0) in vec3 a_position;
|
||||
layout(location = 1) in vec3 a_normal;
|
||||
layout(location = 2) in vec2 a_texcoord;
|
||||
layout(location = 3) in vec3 a_tangent;
|
||||
|
||||
// Uniforms
|
||||
uniform mat4 u_model;
|
||||
uniform mat4 u_view;
|
||||
uniform mat4 u_projection;
|
||||
uniform mat3 u_normal_matrix;
|
||||
|
||||
// Outputs to fragment shader
|
||||
// NOTE: We store it as int uniform in C++ for simplicity; GLSL expects uint.
|
||||
uniform uint u_triangle_id_base;
|
||||
|
||||
out vec3 v_world_position;
|
||||
out vec3 v_world_normal;
|
||||
out vec2 v_texcoord;
|
||||
out vec3 v_world_tangent;
|
||||
flat out uint v_triangle_id_base;
|
||||
|
||||
void main() {
|
||||
// Transform position to world space
|
||||
vec4 world_pos = u_model * vec4(a_position, 1.0);
|
||||
v_world_position = world_pos.xyz;
|
||||
|
||||
// Transform normal and tangent to world space
|
||||
v_world_normal = normalize(u_normal_matrix * a_normal);
|
||||
v_world_tangent = normalize(u_normal_matrix * a_tangent);
|
||||
|
||||
// Pass through texture coordinates
|
||||
v_texcoord = a_texcoord;
|
||||
v_triangle_id_base = u_triangle_id_base;
|
||||
|
||||
// Transform to clip space
|
||||
gl_Position = u_projection * u_view * world_pos;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* @brief Implementation of BVH class (optimized version)
|
||||
*/
|
||||
|
||||
#include <stack>
|
||||
#include <algorithm>
|
||||
#include <are/acceleration/bvh.h>
|
||||
#include <are/core/logger.h>
|
||||
|
|
@ -72,6 +73,53 @@ bool BVH::intersect_any(const Ray &ray, Real t_max) const {
|
|||
return intersect_any_iterative(ray, t_max);
|
||||
}
|
||||
|
||||
bool BVH::intersect_any(const Ray& ray, Real t_max, uint32_t ignore_triangle_index) const {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
if (!is_built()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// iterative traversal is safer than recursion for deep trees, but keep style consistent
|
||||
std::stack<uint32_t> stack;
|
||||
stack.push(root_index_);
|
||||
|
||||
while (!stack.empty()) {
|
||||
uint32_t node_index = stack.top();
|
||||
stack.pop();
|
||||
|
||||
const BVHNode& node = nodes_[node_index];
|
||||
|
||||
Real t0, t1;
|
||||
if (!node.bounds_.intersect_ray(ray, t0, t1)) {
|
||||
continue;
|
||||
}
|
||||
if (t0 > t_max) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.is_leaf()) {
|
||||
for (uint32_t i = 0; i < node.primitive_count_; ++i) {
|
||||
uint32_t prim_idx = primitive_indices_[node.first_primitive_ + i];
|
||||
if (prim_idx == ignore_triangle_index) {
|
||||
continue;
|
||||
}
|
||||
if (prim_idx >= triangles_.size()) {
|
||||
continue;
|
||||
}
|
||||
if (triangles_[prim_idx].intersect_fast(ray, t_max)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stack.push(node.left_child_);
|
||||
stack.push(node.right_child_);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t BVH::get_memory_usage() const {
|
||||
size_t total = 0;
|
||||
total += nodes_.size() * sizeof(BVHNode);
|
||||
|
|
|
|||
|
|
@ -18,13 +18,12 @@ GBuffer::GBuffer(int width, int height)
|
|||
, albedo_texture_(0)
|
||||
, material_texture_(0)
|
||||
, depth_texture_(0)
|
||||
, primitive_id_texture_(0)
|
||||
, width_(width)
|
||||
, height_(height) {
|
||||
|
||||
create_textures();
|
||||
create_framebuffer();
|
||||
|
||||
ARE_LOG_INFO("GBuffer: Created " + std::to_string(width) + "x" + std::to_string(height));
|
||||
}
|
||||
|
||||
GBuffer::~GBuffer() {
|
||||
|
|
@ -32,9 +31,12 @@ GBuffer::~GBuffer() {
|
|||
|
||||
if (rbo_depth_ != 0) {
|
||||
glDeleteRenderbuffers(1, &rbo_depth_);
|
||||
rbo_depth_ = 0;
|
||||
}
|
||||
|
||||
if (fbo_ != 0) {
|
||||
glDeleteFramebuffers(1, &fbo_);
|
||||
fbo_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,17 +50,20 @@ void GBuffer::resize(int width, int height) {
|
|||
width_ = width;
|
||||
height_ = height;
|
||||
|
||||
// Recreate textures and framebuffer
|
||||
delete_textures();
|
||||
|
||||
if (rbo_depth_ != 0) {
|
||||
glDeleteRenderbuffers(1, &rbo_depth_);
|
||||
rbo_depth_ = 0;
|
||||
}
|
||||
|
||||
if (fbo_ != 0) {
|
||||
glDeleteFramebuffers(1, &fbo_);
|
||||
fbo_ = 0;
|
||||
}
|
||||
|
||||
create_textures();
|
||||
create_framebuffer();
|
||||
|
||||
ARE_LOG_INFO("GBuffer: Resized to " + std::to_string(width) + "x" + std::to_string(height));
|
||||
}
|
||||
|
||||
void GBuffer::bind() {
|
||||
|
|
@ -85,6 +90,7 @@ void GBuffer::bind_texture(int index, int texture_unit) {
|
|||
case 2: glBindTexture(GL_TEXTURE_2D, albedo_texture_); break;
|
||||
case 3: glBindTexture(GL_TEXTURE_2D, material_texture_); break;
|
||||
case 4: glBindTexture(GL_TEXTURE_2D, depth_texture_); break;
|
||||
case 5: glBindTexture(GL_TEXTURE_2D, primitive_id_texture_); break;
|
||||
default:
|
||||
ARE_LOG_WARN("GBuffer: Invalid texture index " + std::to_string(index));
|
||||
break;
|
||||
|
|
@ -94,54 +100,60 @@ void GBuffer::bind_texture(int index, int texture_unit) {
|
|||
void GBuffer::read_pixels(int index, void* data) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
bind();
|
||||
// Robust: read from texture object (not from FBO read buffer)
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
|
||||
|
||||
GLenum attachment;
|
||||
GLenum format;
|
||||
GLenum type;
|
||||
uint32_t tex = 0;
|
||||
GLenum format = GL_RGBA;
|
||||
GLenum type = GL_UNSIGNED_BYTE;
|
||||
|
||||
switch (index) {
|
||||
case 0: // Position
|
||||
attachment = GL_COLOR_ATTACHMENT0;
|
||||
case 0:
|
||||
tex = position_texture_;
|
||||
format = GL_RGB;
|
||||
type = GL_FLOAT;
|
||||
break;
|
||||
case 1: // Normal
|
||||
attachment = GL_COLOR_ATTACHMENT1;
|
||||
case 1:
|
||||
tex = normal_texture_;
|
||||
format = GL_RGB;
|
||||
type = GL_FLOAT;
|
||||
break;
|
||||
case 2: // Albedo
|
||||
attachment = GL_COLOR_ATTACHMENT2;
|
||||
case 2:
|
||||
tex = albedo_texture_;
|
||||
format = GL_RGBA;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case 3: // Material
|
||||
attachment = GL_COLOR_ATTACHMENT3;
|
||||
case 3:
|
||||
tex = material_texture_;
|
||||
format = GL_RG;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case 4: // Depth
|
||||
attachment = GL_DEPTH_ATTACHMENT;
|
||||
case 4:
|
||||
tex = depth_texture_;
|
||||
format = GL_DEPTH_COMPONENT;
|
||||
type = GL_FLOAT;
|
||||
break;
|
||||
case 5:
|
||||
tex = primitive_id_texture_;
|
||||
format = GL_RED_INTEGER;
|
||||
type = GL_UNSIGNED_INT;
|
||||
break;
|
||||
default:
|
||||
ARE_LOG_ERROR("GBuffer: Invalid buffer index for read_pixels");
|
||||
unbind();
|
||||
return;
|
||||
}
|
||||
|
||||
glReadBuffer(attachment);
|
||||
glReadPixels(0, 0, width_, height_, format, type, data);
|
||||
|
||||
unbind();
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, format, type, data);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void GBuffer::create_textures() {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
// Position texture (RGB16F)
|
||||
glGenTextures(1, &position_texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, position_texture_);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width_, height_, 0, GL_RGB, GL_FLOAT, nullptr);
|
||||
|
|
@ -150,7 +162,6 @@ void GBuffer::create_textures() {
|
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Normal texture (RGB16F)
|
||||
glGenTextures(1, &normal_texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, normal_texture_);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width_, height_, 0, GL_RGB, GL_FLOAT, nullptr);
|
||||
|
|
@ -159,7 +170,6 @@ void GBuffer::create_textures() {
|
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Albedo + Metallic texture (RGBA8)
|
||||
glGenTextures(1, &albedo_texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, albedo_texture_);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width_, height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
|
|
@ -168,7 +178,6 @@ void GBuffer::create_textures() {
|
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Roughness + AO texture (RG8)
|
||||
glGenTextures(1, &material_texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, material_texture_);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width_, height_, 0, GL_RG, GL_UNSIGNED_BYTE, nullptr);
|
||||
|
|
@ -177,10 +186,17 @@ void GBuffer::create_textures() {
|
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Depth texture (R32F)
|
||||
glGenTextures(1, &depth_texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, depth_texture_);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, width_, height_, 0, GL_RED, GL_FLOAT, nullptr);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width_, height_, 0, GL_DEPTH_COMPONENT, 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);
|
||||
|
||||
glGenTextures(1, &primitive_id_texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, primitive_id_texture_);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, width_, height_, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, 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);
|
||||
|
|
@ -190,60 +206,47 @@ void GBuffer::create_textures() {
|
|||
}
|
||||
|
||||
void GBuffer::delete_textures() {
|
||||
if (position_texture_ != 0) {
|
||||
glDeleteTextures(1, &position_texture_);
|
||||
position_texture_ = 0;
|
||||
}
|
||||
if (normal_texture_ != 0) {
|
||||
glDeleteTextures(1, &normal_texture_);
|
||||
normal_texture_ = 0;
|
||||
}
|
||||
if (albedo_texture_ != 0) {
|
||||
glDeleteTextures(1, &albedo_texture_);
|
||||
albedo_texture_ = 0;
|
||||
}
|
||||
if (material_texture_ != 0) {
|
||||
glDeleteTextures(1, &material_texture_);
|
||||
material_texture_ = 0;
|
||||
}
|
||||
if (depth_texture_ != 0) {
|
||||
glDeleteTextures(1, &depth_texture_);
|
||||
depth_texture_ = 0;
|
||||
}
|
||||
if (position_texture_ != 0) glDeleteTextures(1, &position_texture_);
|
||||
if (normal_texture_ != 0) glDeleteTextures(1, &normal_texture_);
|
||||
if (albedo_texture_ != 0) glDeleteTextures(1, &albedo_texture_);
|
||||
if (material_texture_ != 0) glDeleteTextures(1, &material_texture_);
|
||||
if (depth_texture_ != 0) glDeleteTextures(1, &depth_texture_);
|
||||
if (primitive_id_texture_ != 0) glDeleteTextures(1, &primitive_id_texture_);
|
||||
|
||||
position_texture_ = 0;
|
||||
normal_texture_ = 0;
|
||||
albedo_texture_ = 0;
|
||||
material_texture_ = 0;
|
||||
depth_texture_ = 0;
|
||||
primitive_id_texture_ = 0;
|
||||
}
|
||||
|
||||
void GBuffer::create_framebuffer() {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
// Create framebuffer
|
||||
glGenFramebuffers(1, &fbo_);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||
|
||||
// Attach textures
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, position_texture_, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, normal_texture_, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, albedo_texture_, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, material_texture_, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, GL_TEXTURE_2D, primitive_id_texture_, 0);
|
||||
|
||||
// Specify draw buffers
|
||||
GLenum draw_buffers[] = {
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_COLOR_ATTACHMENT1,
|
||||
GL_COLOR_ATTACHMENT2,
|
||||
GL_COLOR_ATTACHMENT3
|
||||
GL_COLOR_ATTACHMENT3,
|
||||
GL_COLOR_ATTACHMENT4
|
||||
};
|
||||
glDrawBuffers(4, draw_buffers);
|
||||
glDrawBuffers(5, draw_buffers);
|
||||
|
||||
// Create depth renderbuffer
|
||||
glGenRenderbuffers(1, &rbo_depth_);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth_);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width_, height_);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo_depth_);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture_, 0);
|
||||
|
||||
// Check framebuffer completeness
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
ARE_LOG_ERROR("GBuffer: Framebuffer is not complete! Status: " + std::to_string(status));
|
||||
ARE_LOG_ERROR("GBuffer: Framebuffer incomplete. Status=" + std::to_string(status));
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
|
|
|||
|
|
@ -14,153 +14,118 @@
|
|||
#include <are/core/logger.h>
|
||||
#include <are/core/profiler.h>
|
||||
#include <are/platform/gl_context.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
|
||||
namespace are {
|
||||
|
||||
Rasterizer::Rasterizer(int width, int height)
|
||||
: width_(width)
|
||||
: gbuffer_(std::make_unique<GBuffer>(width, height))
|
||||
, gbuffer_shader_(std::make_unique<ShaderProgram>())
|
||||
, triangle_base_provider_(nullptr)
|
||||
, state_()
|
||||
, width_(width)
|
||||
, height_(height) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
// Create G-Buffer
|
||||
gbuffer_ = std::make_unique<GBuffer>(width, height);
|
||||
|
||||
// Create shader program
|
||||
gbuffer_shader_ = std::make_unique<ShaderProgram>();
|
||||
|
||||
ARE_LOG_INFO("Rasterizer: Created " + std::to_string(width) + "x" + std::to_string(height));
|
||||
}
|
||||
|
||||
Rasterizer::~Rasterizer() {
|
||||
ARE_LOG_INFO("Rasterizer: Destroyed");
|
||||
Rasterizer::~Rasterizer() = default;
|
||||
|
||||
void Rasterizer::set_state(const RasterizerState& state) {
|
||||
state_ = state;
|
||||
}
|
||||
|
||||
void Rasterizer::set_triangle_base_provider(std::function<uint32_t(size_t)> provider) {
|
||||
triangle_base_provider_ = std::move(provider);
|
||||
}
|
||||
|
||||
void Rasterizer::resize(int width, int height) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
if (width == width_ && height == height_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (width == width_ && height == height_) return;
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
|
||||
if (gbuffer_) {
|
||||
gbuffer_->resize(width, height);
|
||||
}
|
||||
|
||||
ARE_LOG_INFO("Rasterizer: Resized to " + std::to_string(width) + "x" + std::to_string(height));
|
||||
gbuffer_->resize(width_, height_);
|
||||
}
|
||||
|
||||
void Rasterizer::render_gbuffer(const SceneManager& scene, const Camera& camera) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
if (!gbuffer_shader_ || !gbuffer_shader_->is_valid()) {
|
||||
ARE_LOG_ERROR("Rasterizer: G-Buffer shader is not valid");
|
||||
ARE_LOG_ERROR("Rasterizer: gbuffer shader not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind G-Buffer for rendering
|
||||
gbuffer_->bind();
|
||||
|
||||
// Clear buffers
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Enable depth testing
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
if (state_.enable_depth_test) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
} else {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
// Enable face culling
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
glFrontFace(GL_CCW);
|
||||
if (state_.enable_cull_face) {
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(static_cast<GLenum>(state_.cull_face_mode));
|
||||
glFrontFace(static_cast<GLenum>(state_.front_face));
|
||||
} else {
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
// Use G-Buffer shader
|
||||
gbuffer_shader_->use();
|
||||
|
||||
// Set view and projection matrices
|
||||
gbuffer_shader_->set_uniform("u_view", camera.get_view_matrix());
|
||||
gbuffer_shader_->set_uniform("u_projection", camera.get_projection_matrix());
|
||||
|
||||
// Render all meshes
|
||||
const auto& meshes = scene.get_all_meshes();
|
||||
const auto& materials = scene.get_all_materials();
|
||||
|
||||
for (const auto& mesh : meshes) {
|
||||
if (mesh.is_empty() || !mesh.has_gpu_resources()) {
|
||||
continue;
|
||||
}
|
||||
for (size_t mi = 0; mi < meshes.size(); ++mi) {
|
||||
const auto& mesh = meshes[mi];
|
||||
if (mesh.is_empty() || !mesh.has_gpu_resources()) continue;
|
||||
|
||||
// Set model matrix (identity for now, can be extended with Transform)
|
||||
Mat4 model_matrix = Mat4(1.0f);
|
||||
gbuffer_shader_->set_uniform("u_model", model_matrix);
|
||||
Mat4 model = Mat4(1.0f);
|
||||
gbuffer_shader_->set_uniform("u_model", model);
|
||||
|
||||
// Calculate normal matrix
|
||||
Mat3 normal_matrix = glm::transpose(glm::inverse(Mat3(model_matrix)));
|
||||
Mat3 normal_matrix = glm::inverseTranspose(Mat3(model));
|
||||
gbuffer_shader_->set_uniform("u_normal_matrix", normal_matrix);
|
||||
|
||||
// Set material properties
|
||||
MaterialHandle mat_handle = mesh.get_material();
|
||||
if (mat_handle != are_invalid_handle && mat_handle <= materials.size()) {
|
||||
const Material& material = materials[mat_handle - 1]; // Handle is 1-based
|
||||
gbuffer_shader_->set_uniform("u_albedo", material.get_albedo());
|
||||
gbuffer_shader_->set_uniform("u_metallic", material.get_metallic());
|
||||
gbuffer_shader_->set_uniform("u_roughness", material.get_roughness());
|
||||
uint32_t tri_base = triangle_base_provider_ ? triangle_base_provider_(mi) : 0u;
|
||||
// IMPORTANT: u_triangle_id_base is uint in GLSL, must use glUniform1ui
|
||||
gbuffer_shader_->set_uniform("u_triangle_id_base", tri_base);
|
||||
|
||||
const Material* mat = scene.get_material(mesh.get_material());
|
||||
if (mat) {
|
||||
gbuffer_shader_->set_uniform("u_albedo", mat->get_albedo());
|
||||
gbuffer_shader_->set_uniform("u_metallic", mat->get_metallic());
|
||||
gbuffer_shader_->set_uniform("u_roughness", mat->get_roughness());
|
||||
} else {
|
||||
// Default material
|
||||
gbuffer_shader_->set_uniform("u_albedo", Vec3(0.8f, 0.8f, 0.8f));
|
||||
gbuffer_shader_->set_uniform("u_albedo", Vec3(0.8f));
|
||||
gbuffer_shader_->set_uniform("u_metallic", 0.0f);
|
||||
gbuffer_shader_->set_uniform("u_roughness", 0.5f);
|
||||
}
|
||||
|
||||
// Draw mesh
|
||||
glBindVertexArray(mesh.get_vao());
|
||||
glDrawElements(GL_TRIANGLES,
|
||||
static_cast<GLsizei>(mesh.get_index_count()),
|
||||
GL_UNSIGNED_INT,
|
||||
nullptr);
|
||||
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mesh.get_index_count()), GL_UNSIGNED_INT, nullptr);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
// Disable states
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
// Unbind G-Buffer
|
||||
gbuffer_->unbind();
|
||||
|
||||
ARE_GL_CHECK();
|
||||
}
|
||||
|
||||
GBuffer& Rasterizer::get_gbuffer() {
|
||||
return *gbuffer_;
|
||||
}
|
||||
|
||||
const GBuffer& Rasterizer::get_gbuffer() const {
|
||||
return *gbuffer_;
|
||||
}
|
||||
GBuffer& Rasterizer::get_gbuffer() { return *gbuffer_; }
|
||||
const GBuffer& Rasterizer::get_gbuffer() const { return *gbuffer_; }
|
||||
|
||||
void Rasterizer::upload_mesh(Mesh& mesh) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
if (mesh.is_empty()) {
|
||||
ARE_LOG_WARN("Rasterizer: Attempting to upload empty mesh");
|
||||
ARE_LOG_WARN("Rasterizer: upload_mesh on empty mesh");
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete existing GPU resources if any
|
||||
if (mesh.has_gpu_resources()) {
|
||||
delete_mesh(mesh);
|
||||
}
|
||||
|
||||
if (mesh.has_gpu_resources()) delete_mesh(mesh);
|
||||
setup_mesh_buffers(mesh);
|
||||
|
||||
ARE_LOG_DEBUG("Rasterizer: Uploaded mesh with " +
|
||||
std::to_string(mesh.get_vertex_count()) + " vertices, " +
|
||||
std::to_string(mesh.get_triangle_count()) + " triangles");
|
||||
}
|
||||
|
||||
void Rasterizer::delete_mesh(Mesh& mesh) {
|
||||
|
|
@ -170,15 +135,9 @@ void Rasterizer::delete_mesh(Mesh& mesh) {
|
|||
uint32_t vbo = mesh.get_vbo();
|
||||
uint32_t ebo = mesh.get_ebo();
|
||||
|
||||
if (vao != 0) {
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
}
|
||||
if (vbo != 0) {
|
||||
glDeleteBuffers(1, &vbo);
|
||||
}
|
||||
if (ebo != 0) {
|
||||
glDeleteBuffers(1, &ebo);
|
||||
}
|
||||
if (vao) glDeleteVertexArrays(1, &vao);
|
||||
if (vbo) glDeleteBuffers(1, &vbo);
|
||||
if (ebo) glDeleteBuffers(1, &ebo);
|
||||
|
||||
mesh.set_vao(0);
|
||||
mesh.set_vbo(0);
|
||||
|
|
@ -188,89 +147,46 @@ void Rasterizer::delete_mesh(Mesh& mesh) {
|
|||
void Rasterizer::initialize_shaders(const std::string& shader_dir) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
if (!gbuffer_shader_) {
|
||||
gbuffer_shader_ = std::make_unique<ShaderProgram>();
|
||||
}
|
||||
bool ok = true;
|
||||
ok &= gbuffer_shader_->load_shader(ShaderType::ARE_SHADER_VERTEX, shader_dir + "gbuffer/gbuffer.vert");
|
||||
ok &= gbuffer_shader_->load_shader(ShaderType::ARE_SHADER_FRAGMENT, shader_dir + "gbuffer/gbuffer.frag");
|
||||
ok &= gbuffer_shader_->link();
|
||||
|
||||
std::string vert_path = shader_dir + "gbuffer/gbuffer.vert";
|
||||
std::string frag_path = shader_dir + "gbuffer/gbuffer.frag";
|
||||
|
||||
bool success = true;
|
||||
|
||||
if (!gbuffer_shader_->load_shader(ShaderType::ARE_SHADER_VERTEX, vert_path)) {
|
||||
ARE_LOG_ERROR("Rasterizer: Failed to load vertex shader: " + vert_path);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!gbuffer_shader_->load_shader(ShaderType::ARE_SHADER_FRAGMENT, frag_path)) {
|
||||
ARE_LOG_ERROR("Rasterizer: Failed to load fragment shader: " + frag_path);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success && !gbuffer_shader_->link()) {
|
||||
ARE_LOG_ERROR("Rasterizer: Failed to link G-Buffer shader program");
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
ARE_LOG_INFO("Rasterizer: Shaders initialized successfully");
|
||||
if (!ok) {
|
||||
ARE_LOG_ERROR("Rasterizer: Failed to init gbuffer shaders");
|
||||
}
|
||||
}
|
||||
|
||||
void Rasterizer::setup_mesh_buffers(Mesh& mesh) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
uint32_t vao, vbo, ebo;
|
||||
|
||||
// Create VAO
|
||||
uint32_t vao = 0, vbo = 0, ebo = 0;
|
||||
glGenVertexArrays(1, &vao);
|
||||
glBindVertexArray(vao);
|
||||
|
||||
// Create VBO
|
||||
glGenBuffers(1, &vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
mesh.get_vertex_count() * sizeof(Vertex),
|
||||
mesh.get_vertices().data(),
|
||||
GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, mesh.get_vertex_count() * sizeof(Vertex), mesh.get_vertices().data(), GL_STATIC_DRAW);
|
||||
|
||||
// Create EBO
|
||||
glGenBuffers(1, &ebo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
||||
mesh.get_index_count() * sizeof(uint32_t),
|
||||
mesh.get_indices().data(),
|
||||
GL_STATIC_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.get_index_count() * sizeof(uint32_t), mesh.get_indices().data(), GL_STATIC_DRAW);
|
||||
|
||||
// Setup vertex attributes
|
||||
// Position (location = 0)
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
|
||||
sizeof(Vertex),
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
reinterpret_cast<void*>(get_position_offset()));
|
||||
|
||||
// Normal (location = 1)
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
|
||||
sizeof(Vertex),
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
reinterpret_cast<void*>(get_normal_offset()));
|
||||
|
||||
// Texcoord (location = 2)
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE,
|
||||
sizeof(Vertex),
|
||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
reinterpret_cast<void*>(get_texcoord_offset()));
|
||||
|
||||
// Tangent (location = 3)
|
||||
glEnableVertexAttribArray(3);
|
||||
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE,
|
||||
sizeof(Vertex),
|
||||
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
reinterpret_cast<void*>(get_tangent_offset()));
|
||||
|
||||
// Unbind VAO
|
||||
glBindVertexArray(0);
|
||||
|
||||
// Store handles in mesh
|
||||
mesh.set_vao(vao);
|
||||
mesh.set_vbo(vbo);
|
||||
mesh.set_ebo(ebo);
|
||||
|
|
|
|||
|
|
@ -155,8 +155,12 @@ void ShaderProgram::use() const {
|
|||
}
|
||||
}
|
||||
|
||||
void ShaderProgram::set_uniform(const std::string& name, uint32_t value) {
|
||||
glUniform1ui(get_uniform_location(name), value);
|
||||
}
|
||||
|
||||
void ShaderProgram::set_uniform(const std::string& name, int value) {
|
||||
glUniform1i(get_uniform_location(name), value);
|
||||
glUniform1ui(get_uniform_location(name), value);
|
||||
}
|
||||
|
||||
void ShaderProgram::set_uniform(const std::string& name, float value) {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
/**
|
||||
* @file cpu_raytracer.cpp
|
||||
* @brief Implementation of CPURayTracer
|
||||
* @brief CPU hybrid ray tracer (GBuffer-driven) with geometric normal offset
|
||||
*/
|
||||
|
||||
#include <are/raytracer/cpu_raytracer.h>
|
||||
|
||||
#include <are/acceleration/bvh.h>
|
||||
#include <are/scene/scene_manager.h>
|
||||
#include <are/scene/camera.h>
|
||||
#include <are/scene/material.h>
|
||||
#include <are/scene/light.h>
|
||||
#include <are/scene/directional_light.h>
|
||||
#include <are/scene/point_light.h>
|
||||
|
|
@ -26,34 +24,37 @@
|
|||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
// #ifdef ARE_USE_OPENMP
|
||||
// #include <omp.h>
|
||||
// #endif
|
||||
#include <vector>
|
||||
|
||||
namespace are {
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief Apply simple Reinhard tonemapping.
|
||||
* @param c HDR color
|
||||
* @param exposure Exposure value
|
||||
* @return LDR color in [0,1]
|
||||
*/
|
||||
inline Real compute_ray_epsilon(const Vec3& p) {
|
||||
Real s = std::max({std::abs(p.x), std::abs(p.y), std::abs(p.z), 1.0f});
|
||||
return 1e-4f * s;
|
||||
}
|
||||
|
||||
inline Vec3 offset_ray_origin(const Vec3& p, const Vec3& ng) {
|
||||
Real eps = compute_ray_epsilon(p);
|
||||
return p + ng * (eps * 4.0f);
|
||||
}
|
||||
|
||||
inline Vec3 tonemap_reinhard(const Vec3& c, Real exposure) {
|
||||
Vec3 x = c * exposure;
|
||||
return x / (Vec3(1.0f) + x);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Offset ray origin to reduce self-intersection.
|
||||
* @param p Hit position
|
||||
* @param n Shading normal
|
||||
* @return Offset position
|
||||
*/
|
||||
inline Vec3 offset_ray_origin(const Vec3& p, const Vec3& n) {
|
||||
return p + n * (are_epsilon * 10.0f);
|
||||
inline Vec3 decode_albedo_from_rgba8(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return Vec3(r, g, b) / 255.0f;
|
||||
}
|
||||
|
||||
inline Real decode_01_from_u8(uint8_t v) {
|
||||
return static_cast<Real>(v) / 255.0f;
|
||||
}
|
||||
|
||||
inline bool finite_vec3(const Vec3& v) {
|
||||
return std::isfinite(v.x) && std::isfinite(v.y) && std::isfinite(v.z);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -78,6 +79,7 @@ void CPURayTracer::render(const SceneManager& scene,
|
|||
const GBuffer* gbuffer,
|
||||
uint32_t output_texture) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
(void)camera;
|
||||
|
||||
if (!bvh_ || !bvh_->is_built()) {
|
||||
ARE_LOG_ERROR("CPURayTracer: BVH is null or not built");
|
||||
|
|
@ -85,8 +87,8 @@ void CPURayTracer::render(const SceneManager& scene,
|
|||
}
|
||||
|
||||
if (!gbuffer) {
|
||||
ARE_LOG_CRITICAL("CPURayTracer: GBuffer is null, cannot infer render resolution");
|
||||
throw std::runtime_error("CPURayTracer requires a valid GBuffer for resolution");
|
||||
ARE_LOG_CRITICAL("CPURayTracer: GBuffer is null (hybrid requires it)");
|
||||
throw std::runtime_error("CPURayTracer requires GBuffer in hybrid mode");
|
||||
}
|
||||
|
||||
if (output_texture == 0) {
|
||||
|
|
@ -99,104 +101,142 @@ void CPURayTracer::render(const SceneManager& scene,
|
|||
height_ = gbuffer->get_height();
|
||||
|
||||
if (width_ <= 0 || height_ <= 0) {
|
||||
ARE_LOG_ERROR("CPURayTracer: Invalid render resolution");
|
||||
ARE_LOG_ERROR("CPURayTracer: Invalid resolution");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Vec3> pos(static_cast<size_t>(width_ * height_));
|
||||
std::vector<Vec3> nrm(static_cast<size_t>(width_ * height_));
|
||||
std::vector<uint8_t> albedo_metallic(static_cast<size_t>(width_ * height_ * 4));
|
||||
std::vector<uint8_t> rough_ao(static_cast<size_t>(width_ * height_ * 2));
|
||||
std::vector<Real> depth(static_cast<size_t>(width_ * height_));
|
||||
std::vector<uint32_t> prim_id(static_cast<size_t>(width_ * height_));
|
||||
|
||||
const_cast<GBuffer *>(gbuffer)->read_pixels(0, pos.data());
|
||||
const_cast<GBuffer *>(gbuffer)->read_pixels(1, nrm.data());
|
||||
const_cast<GBuffer *>(gbuffer)->read_pixels(2, albedo_metallic.data());
|
||||
const_cast<GBuffer *>(gbuffer)->read_pixels(3, rough_ao.data());
|
||||
const_cast<GBuffer *>(gbuffer)->read_pixels(4, depth.data());
|
||||
const_cast<GBuffer *>(gbuffer)->read_pixels(5, prim_id.data());
|
||||
|
||||
framebuffer_.assign(static_cast<size_t>(width_ * height_), Vec3(0.0f));
|
||||
|
||||
const int spp = std::max(1, config_.spp);
|
||||
const int max_depth = std::max(1, config_.max_depth);
|
||||
const auto& triangles = bvh_->get_triangles();
|
||||
|
||||
framebuffer_.assign(static_cast<size_t>(width_ * height_), Vec4(0.0f));
|
||||
|
||||
// #ifdef ARE_USE_OPENMP
|
||||
// #pragma omp parallel for schedule(dynamic, 1)
|
||||
// #endif
|
||||
for (int y = 0; y < height_; ++y) {
|
||||
RandomGenerator& rng = get_thread_random();
|
||||
|
||||
for (int x = 0; x < width_; ++x) {
|
||||
Vec3 hdr(0.0f);
|
||||
const size_t idx = static_cast<size_t>(y * width_ + x);
|
||||
|
||||
for (int s = 0; s < spp; ++s) {
|
||||
Real u = (static_cast<Real>(x) + rng.random_float()) / static_cast<Real>(width_);
|
||||
Real v = (static_cast<Real>(y) + rng.random_float()) / static_cast<Real>(height_);
|
||||
|
||||
Vec3 origin;
|
||||
Vec3 direction;
|
||||
camera.generate_ray(u, v, origin, direction);
|
||||
|
||||
Ray ray(origin, direction, are_epsilon, 1e30f);
|
||||
hdr += trace_ray(ray, max_depth);
|
||||
// Depth validity
|
||||
if (!(depth[idx] > 0.0f && depth[idx] < 0.999999f)) {
|
||||
framebuffer_[idx] = Vec3(0.0f);
|
||||
continue;
|
||||
}
|
||||
|
||||
hdr /= static_cast<Real>(spp);
|
||||
Vec3 P = pos[idx];
|
||||
Vec3 Ns = glm::normalize(nrm[idx]);
|
||||
|
||||
// Phase 5: tonemap in tracer for standalone output
|
||||
Vec3 ldr = tonemap_reinhard(hdr, 1.0f);
|
||||
framebuffer_[static_cast<size_t>(y * width_ + x)] = Vec4(ldr, 1.0f);
|
||||
if (!finite_vec3(P) || !finite_vec3(Ns) || glm::length(Ns) < 0.1f) {
|
||||
framebuffer_[idx] = Vec3(0.0f);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Geometric normal from primitive id
|
||||
Vec3 Ng = Ns;
|
||||
uint32_t pid = prim_id[idx];
|
||||
if (pid < triangles.size()) {
|
||||
Ng = triangles[pid].normal();
|
||||
}
|
||||
|
||||
const uint8_t* am = &albedo_metallic[idx * 4];
|
||||
Vec3 albedo = decode_albedo_from_rgba8(am[0], am[1], am[2]);
|
||||
(void)decode_01_from_u8(am[3]);
|
||||
|
||||
const uint8_t* ra = &rough_ao[idx * 2];
|
||||
(void)decode_01_from_u8(ra[0]);
|
||||
Real ao_gbuffer = decode_01_from_u8(ra[1]);
|
||||
|
||||
Vec3 accum(0.0f);
|
||||
|
||||
for (int s = 0; s < spp; ++s) {
|
||||
HitRecord surf;
|
||||
surf.position_ = P;
|
||||
surf.normal_ = Ns;
|
||||
surf.t_ = 1.0f;
|
||||
surf.material_ = are_invalid_handle;
|
||||
|
||||
// Direct lighting (shadow uses robust epsilon)
|
||||
Vec3 direct = compute_direct_lighting(surf);
|
||||
|
||||
// AO
|
||||
Real ao = 1.0f;
|
||||
if (config_.enable_ao) {
|
||||
ao = compute_ambient_occlusion(surf);
|
||||
}
|
||||
ao *= ao_gbuffer;
|
||||
|
||||
// GI (simplified)
|
||||
Vec3 gi(0.0f);
|
||||
if (config_.enable_gi && max_depth > 1) {
|
||||
Vec3 bounce_dir = rng.random_cosine_direction(Ns);
|
||||
Vec3 origin = offset_ray_origin(P, Ng);
|
||||
Real eps = compute_ray_epsilon(P);
|
||||
Ray bounce(origin, bounce_dir, eps * 4.0f, 1e30f);
|
||||
gi = trace_ray(bounce, max_depth - 1);
|
||||
}
|
||||
|
||||
Vec3 c = albedo * direct * ao + albedo * gi;
|
||||
accum += c;
|
||||
}
|
||||
|
||||
Vec3 hdr = accum / static_cast<Real>(spp);
|
||||
framebuffer_[idx] = tonemap_reinhard(hdr, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// Upload to output texture (recommended internal format: GL_RGBA16F)
|
||||
std::vector<Vec4> rgba(static_cast<size_t>(width_ * height_));
|
||||
for (size_t i = 0; i < rgba.size(); ++i) {
|
||||
rgba[i] = Vec4(framebuffer_[i], 1.0f);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, output_texture);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_RGBA, GL_FLOAT, framebuffer_.data());
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_RGBA, GL_FLOAT, rgba.data());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
Vec3 CPURayTracer::trace_ray(const Ray& ray, int depth) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
if (depth <= 0) {
|
||||
return Vec3(0.0f);
|
||||
}
|
||||
|
||||
HitRecord hit;
|
||||
if (!bvh_->intersect(ray, hit)) {
|
||||
// Simple sky
|
||||
Vec3 unit_dir = glm::normalize(ray.direction_);
|
||||
Real t = 0.5f * (unit_dir.y + 1.0f);
|
||||
return lerp(Vec3(1.0f), Vec3(0.5f, 0.7f, 1.0f), t);
|
||||
return Vec3(0.0f);
|
||||
}
|
||||
|
||||
return shade(hit, ray, depth);
|
||||
}
|
||||
|
||||
Vec3 CPURayTracer::shade(const HitRecord& hit, const Ray& ray, int depth) {
|
||||
Vec3 albedo(0.8f);
|
||||
if (scene_) {
|
||||
const Material* mat = scene_->get_material(hit.material_);
|
||||
if (mat) {
|
||||
albedo = mat->get_albedo();
|
||||
}
|
||||
Vec3 Ng = hit.normal_;
|
||||
if (hit.triangle_index_ < bvh_->get_triangles().size()) {
|
||||
Ng = bvh_->get_triangles()[hit.triangle_index_].normal();
|
||||
}
|
||||
|
||||
Vec3 direct = compute_direct_lighting(hit);
|
||||
Real ao = 1.0f;
|
||||
if (config_.enable_ao) {
|
||||
ao = compute_ambient_occlusion(hit);
|
||||
}
|
||||
RandomGenerator& rng = get_thread_random();
|
||||
Vec3 dir = rng.random_cosine_direction(hit.normal_);
|
||||
Vec3 origin = offset_ray_origin(hit.position_, Ng);
|
||||
Real eps = compute_ray_epsilon(hit.position_);
|
||||
|
||||
// Direct term (Lambert)
|
||||
Vec3 Lo = albedo * direct * ao;
|
||||
|
||||
// Diffuse GI (cosine-weighted)
|
||||
if (config_.enable_gi && depth > 1) {
|
||||
RandomGenerator& rng = get_thread_random();
|
||||
Vec3 bounce_dir = rng.random_cosine_direction(hit.normal_);
|
||||
Ray bounce_ray(offset_ray_origin(hit.position_, hit.normal_), bounce_dir, are_epsilon, 1e30f);
|
||||
|
||||
Vec3 Li = trace_ray(bounce_ray, depth - 1);
|
||||
Lo += albedo * Li;
|
||||
}
|
||||
|
||||
(void)ray;
|
||||
return Lo;
|
||||
Ray bounce(origin, dir, eps * 4.0f, 1e30f);
|
||||
return trace_ray(bounce, depth - 1);
|
||||
}
|
||||
|
||||
Vec3 CPURayTracer::compute_direct_lighting(const HitRecord& hit) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
Vec3 lighting(0.0f);
|
||||
if (!scene_) {
|
||||
return lighting;
|
||||
|
|
@ -204,15 +244,13 @@ Vec3 CPURayTracer::compute_direct_lighting(const HitRecord& hit) {
|
|||
|
||||
const auto& lights = scene_->get_all_lights();
|
||||
for (const auto& light_ptr : lights) {
|
||||
if (!light_ptr) {
|
||||
continue;
|
||||
}
|
||||
if (!light_ptr) continue;
|
||||
|
||||
Vec3 L(0.0f);
|
||||
Real max_distance = 1e30f;
|
||||
Real attenuation = 1.0f;
|
||||
|
||||
const LightType type = light_ptr->get_type();
|
||||
LightType type = light_ptr->get_type();
|
||||
|
||||
if (type == LightType::ARE_LIGHT_DIRECTIONAL) {
|
||||
const auto* dl = static_cast<const DirectionalLight*>(light_ptr.get());
|
||||
|
|
@ -221,12 +259,8 @@ Vec3 CPURayTracer::compute_direct_lighting(const HitRecord& hit) {
|
|||
const auto* pl = static_cast<const PointLight*>(light_ptr.get());
|
||||
Vec3 to_light = pl->get_position() - hit.position_;
|
||||
Real dist = glm::length(to_light);
|
||||
if (dist < are_epsilon) {
|
||||
continue;
|
||||
}
|
||||
if (!pl->affects_point(hit.position_)) {
|
||||
continue;
|
||||
}
|
||||
if (dist < are_epsilon) continue;
|
||||
if (!pl->affects_point(hit.position_)) continue;
|
||||
L = to_light / dist;
|
||||
max_distance = dist;
|
||||
attenuation = pl->calculate_attenuation(dist);
|
||||
|
|
@ -234,32 +268,27 @@ Vec3 CPURayTracer::compute_direct_lighting(const HitRecord& hit) {
|
|||
const auto* sl = static_cast<const SpotLight*>(light_ptr.get());
|
||||
Vec3 to_light = sl->get_position() - hit.position_;
|
||||
Real dist = glm::length(to_light);
|
||||
if (dist < are_epsilon) {
|
||||
continue;
|
||||
}
|
||||
if (!sl->affects_point(hit.position_)) {
|
||||
continue;
|
||||
}
|
||||
if (dist < are_epsilon) continue;
|
||||
if (!sl->affects_point(hit.position_)) continue;
|
||||
L = to_light / dist;
|
||||
max_distance = dist;
|
||||
|
||||
Vec3 light_to_point = glm::normalize(hit.position_ - sl->get_position());
|
||||
Real spot = sl->calculate_spot_factor(light_to_point);
|
||||
attenuation *= spot;
|
||||
attenuation *= sl->calculate_spot_factor(light_to_point);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (light_ptr->get_cast_shadows()) {
|
||||
if (is_in_shadow(offset_ray_origin(hit.position_, hit.normal_), L, max_distance)) {
|
||||
Real eps = compute_ray_epsilon(hit.position_);
|
||||
Vec3 origin = hit.position_ + hit.normal_ * (eps * 4.0f);
|
||||
Ray shadow(origin, L, eps * 4.0f, max_distance);
|
||||
if (bvh_ && bvh_->intersect_any(shadow, max_distance)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Real n_dot_l = std::max(0.0f, glm::dot(hit.normal_, L));
|
||||
if (n_dot_l <= 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if (n_dot_l <= 0.0f) continue;
|
||||
|
||||
Vec3 radiance = light_ptr->get_color() * light_ptr->get_intensity();
|
||||
lighting += radiance * n_dot_l * attenuation;
|
||||
|
|
@ -269,8 +298,6 @@ Vec3 CPURayTracer::compute_direct_lighting(const HitRecord& hit) {
|
|||
}
|
||||
|
||||
Real CPURayTracer::compute_ambient_occlusion(const HitRecord& hit) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
if (!bvh_) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
|
@ -283,8 +310,9 @@ Real CPURayTracer::compute_ambient_occlusion(const HitRecord& hit) {
|
|||
int occluded = 0;
|
||||
for (int i = 0; i < ao_samples; ++i) {
|
||||
Vec3 dir = rng.random_in_hemisphere(hit.normal_);
|
||||
Ray ao_ray(offset_ray_origin(hit.position_, hit.normal_), dir, are_epsilon, radius);
|
||||
|
||||
Real eps = compute_ray_epsilon(hit.position_);
|
||||
Vec3 origin = hit.position_ + hit.normal_ * (eps * 4.0f);
|
||||
Ray ao_ray(origin, dir, eps * 4.0f, radius);
|
||||
if (bvh_->intersect_any(ao_ray, radius)) {
|
||||
occluded++;
|
||||
}
|
||||
|
|
@ -294,16 +322,13 @@ Real CPURayTracer::compute_ambient_occlusion(const HitRecord& hit) {
|
|||
return 1.0f - occ;
|
||||
}
|
||||
|
||||
bool CPURayTracer::is_in_shadow(const Vec3& origin, const Vec3& direction, Real max_distance) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
if (!bvh_) {
|
||||
return false;
|
||||
}
|
||||
bool CPURayTracer::is_in_shadow(const Vec3& origin, const Vec3& direction, Real max_distance, uint32_t ignore_triangle) {
|
||||
if (!bvh_) return false;
|
||||
|
||||
Real t_max = (max_distance > 0.0f) ? max_distance : 1e30f;
|
||||
Ray shadow_ray(origin, direction, are_epsilon, t_max);
|
||||
return bvh_->intersect_any(shadow_ray, t_max);
|
||||
Real eps = compute_ray_epsilon(origin);
|
||||
Ray shadow(origin, direction, eps * 4.0f, t_max);
|
||||
return bvh_->intersect_any(shadow, t_max, ignore_triangle);
|
||||
}
|
||||
|
||||
} // namespace are
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* @file geometry_cache.cpp
|
||||
* @brief Implementation of GeometryCache
|
||||
*/
|
||||
|
||||
#include <are/renderer/geometry_cache.h>
|
||||
#include <are/core/logger.h>
|
||||
#include <are/core/profiler.h>
|
||||
|
||||
namespace are {
|
||||
|
||||
static std::vector<Triangle> mesh_to_triangles(const Mesh& mesh) {
|
||||
std::vector<Triangle> tris;
|
||||
if (mesh.is_empty()) {
|
||||
return tris;
|
||||
}
|
||||
|
||||
const auto& v = mesh.get_vertices();
|
||||
const auto& idx = mesh.get_indices();
|
||||
if (idx.size() % 3 != 0) {
|
||||
ARE_LOG_ERROR("GeometryCache: Mesh index count is not multiple of 3");
|
||||
return tris;
|
||||
}
|
||||
|
||||
MaterialHandle material = mesh.get_material();
|
||||
tris.reserve(idx.size() / 3);
|
||||
|
||||
for (size_t i = 0; i < idx.size(); i += 3) {
|
||||
uint32_t i0 = idx[i + 0];
|
||||
uint32_t i1 = idx[i + 1];
|
||||
uint32_t i2 = idx[i + 2];
|
||||
|
||||
if (i0 >= v.size() || i1 >= v.size() || i2 >= v.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tris.emplace_back(v[i0], v[i1], v[i2], material);
|
||||
}
|
||||
|
||||
return tris;
|
||||
}
|
||||
|
||||
bool GeometryCache::build_from_scene(const SceneManager& scene, const BVHBuildConfig& bvh_config) {
|
||||
ARE_PROFILE_FUNCTION();
|
||||
|
||||
triangles_.clear();
|
||||
mesh_triangle_base_.clear();
|
||||
|
||||
const auto& meshes = scene.get_all_meshes();
|
||||
mesh_triangle_base_.reserve(meshes.size());
|
||||
|
||||
uint32_t base = 0;
|
||||
|
||||
for (size_t mi = 0; mi < meshes.size(); ++mi) {
|
||||
mesh_triangle_base_.push_back(base);
|
||||
|
||||
auto tris = mesh_to_triangles(meshes[mi]);
|
||||
base += static_cast<uint32_t>(tris.size());
|
||||
|
||||
triangles_.insert(triangles_.end(), tris.begin(), tris.end());
|
||||
}
|
||||
|
||||
if (triangles_.empty()) {
|
||||
ARE_LOG_WARN("GeometryCache: No triangles in scene");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bvh_.build(triangles_, bvh_config)) {
|
||||
ARE_LOG_ERROR("GeometryCache: BVH build failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
ARE_LOG_INFO("GeometryCache: Built triangles=" + std::to_string(triangles_.size()) +
|
||||
", meshes=" + std::to_string(meshes.size()));
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t GeometryCache::get_mesh_triangle_base(size_t mesh_index) const {
|
||||
if (mesh_index >= mesh_triangle_base_.size()) {
|
||||
return 0;
|
||||
}
|
||||
return mesh_triangle_base_[mesh_index];
|
||||
}
|
||||
|
||||
} // namespace are
|
||||
Loading…
Reference in New Issue