这个架构最后一次commit

master
ternaryop8479 2026-02-09 17:47:21 +08:00
parent 01dd5bd91e
commit 96ffcd4edc
15 changed files with 889 additions and 885 deletions

View File

@ -1,373 +1,282 @@
/** /**
* @file main.cpp * @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/logger.h>
#include <are/core/profiler.h> #include <are/core/profiler.h>
#include <are/platform/gl_context.h>
#include <are/platform/window.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/rasterizer.h>
#include <are/rasterizer/gbuffer.h>
#include <are/rasterizer/shader_program.h> #include <are/rasterizer/shader_program.h>
#include <are/scene/camera.h> #include <are/renderer/geometry_cache.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/acceleration/bvh.h> #include <are/scene/scene_manager.h>
#include <are/geometry/triangle.h> #include <are/scene/camera.h>
#include <are/geometry/vertex.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 <are/raytracer/cpu_raytracer.h>
#include "../lib/glad/glad/glad.h" #include "../lib/glad/glad/glad.h"
#include <glm/glm.hpp>
#include <vector>
#include <string>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <vector>
using namespace are; using namespace are;
namespace { namespace {
/**
* @brief Create a fullscreen quad VAO.
* @return VAO handle
*/
uint32_t create_fullscreen_quad_vao() { uint32_t create_fullscreen_quad_vao() {
float vertices[] = { float vertices[] = {
// pos // uv -1.0f, -1.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.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; glGenBuffers(1, &vbo);
uint32_t vbo = 0; glBindBuffer(GL_ARRAY_BUFFER, vbo);
uint32_t ebo = 0; glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenVertexArrays(1, &vao); glGenBuffers(1, &ebo);
glBindVertexArray(vao); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glGenBuffers(1, &vbo); glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &ebo); glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0); glBindVertexArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0); return vao;
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;
} }
/**
* @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 create_output_texture_rgba16f(int width, int height) {
uint32_t tex = 0; uint32_t tex = 0;
glGenTextures(1, &tex); glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, 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_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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); glBindTexture(GL_TEXTURE_2D, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); return tex;
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;
} }
} // namespace } // namespace
int main() { int main() {
Logger::init(LogLevel::ARE_LOG_INFO); try {
Profiler::init(); Logger::init(LogLevel::ARE_LOG_INFO);
Profiler::init();
WindowConfig wc; WindowConfig wc;
wc.width = 960; wc.width = 960;
wc.height = 540; wc.height = 540;
wc.title = "ARE Phase 5 - CPU Ray Tracing (RGBA16F)"; wc.title = "ARE Phase5 - Hybrid + PrimitiveID";
wc.vsync = true; wc.vsync = true;
Window window(wc); Window window(wc);
if (!GLContext::initialize()) { if (!GLContext::initialize()) {
ARE_LOG_CRITICAL("phase5_test: GLContext::initialize failed"); ARE_LOG_CRITICAL("GLContext initialize failed");
return -1; return -1;
} }
GLContext::print_info();
int fb_w = 0; int fb_w = 0, fb_h = 0;
int fb_h = 0; for (int i = 0; i < 240; ++i) {
window.get_framebuffer_size(fb_w, fb_h); window.poll_events();
if (fb_w <= 0 || fb_h <= 0) { window.get_framebuffer_size(fb_w, fb_h);
ARE_LOG_CRITICAL("phase5_test: framebuffer size is invalid"); if (fb_w > 0 && fb_h > 0) break;
return -1; 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);
Rasterizer rasterizer(fb_w, fb_h); rasterizer.initialize_shaders("shaders/");
GBuffer &gbuffer = rasterizer.get_gbuffer();
// 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 // Display shader (simple)
const char *fsq_vs = R"( const char* vs = R"(
#version 430 core #version 430 core
layout(location = 0) in vec2 a_pos; layout(location = 0) in vec2 a_pos;
layout(location = 1) in vec2 a_uv; layout(location = 1) in vec2 a_uv;
out vec2 v_uv; out vec2 v_uv;
void main() { void main(){ v_uv=a_uv; gl_Position=vec4(a_pos,0,1); }
v_uv = a_uv;
gl_Position = vec4(a_pos, 0.0, 1.0);
}
)"; )";
const char* fs = R"(
const char *fsq_fs = R"(
#version 430 core #version 430 core
in vec2 v_uv; in vec2 v_uv;
out vec4 frag_color; out vec4 frag_color;
uniform sampler2D u_tex; uniform sampler2D u_tex;
void main() { void main(){ frag_color = texture(u_tex, v_uv); }
frag_color = texture(u_tex, v_uv);
}
)"; )";
ShaderProgram display; ShaderProgram display;
if (!display.compile_shader(ShaderType::ARE_SHADER_VERTEX, fsq_vs) || !display.compile_shader(ShaderType::ARE_SHADER_FRAGMENT, fsq_fs) || !display.link()) { if (!display.compile_shader(ShaderType::ARE_SHADER_VERTEX, vs) ||
ARE_LOG_CRITICAL("phase5_test: failed to compile display shader"); !display.compile_shader(ShaderType::ARE_SHADER_FRAGMENT, fs) ||
return -1; !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 Material mat;
SceneManager scene; mat.set_albedo(Vec3(0.8f, 0.2f, 0.2f));
mat.set_roughness(0.6f);
MaterialHandle mat_h = scene.add_material(mat);
Material mat; std::vector<Vertex> ground_v = {
mat.set_albedo(Vec3(0.8f, 0.2f, 0.2f)); Vertex(Vec3(-4, 0, -4), Vec3(0, 1, 0), Vec2(0, 0)),
mat.set_roughness(0.6f); Vertex(Vec3( 4, 0, -4), Vec3(0, 1, 0), Vec2(1, 0)),
MaterialHandle mat_h = scene.add_material(mat); 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> tri_v = {
std::vector<Vertex> ground_v = { Vertex(Vec3(-0.8f, 0.2f, 0.0f), Vec3(0, 1, 0), Vec2(0, 0)),
Vertex(Vec3(-4, 0, -4), Vec3(0, 1, 0), Vec2(0, 0)), Vertex(Vec3( 0.8f, 0.2f, 0.0f), Vec3(0, 1, 0), Vec2(1, 0)),
Vertex(Vec3(4, 0, -4), Vec3(0, 1, 0), Vec2(1, 0)), Vertex(Vec3( 0.0f, 1.2f, 0.3f), Vec3(0, 1, 0), Vec2(0.5f, 1)),
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> tri_i = { 0, 1, 2 };
}; Mesh tri_mesh(tri_v, tri_i, mat_h);
std::vector<uint32_t> ground_i = { 0, 1, 2, 2, 3, 0 }; tri_mesh.compute_tangents();
Mesh ground(ground_v, ground_i, mat_h);
ground.compute_tangents();
MeshHandle ground_mh = scene.add_mesh(ground);
(void)ground_mh;
// A tilted triangle // Upload meshes BEFORE adding (SceneManager stores copy)
std::vector<Vertex> tri_v = { rasterizer.upload_mesh(ground);
Vertex(Vec3(-0.8f, 0.2f, 0.0f), Vec3(0, 0.8f, 0.6f), Vec2(0, 0)), scene.add_mesh(ground);
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);
// Lights rasterizer.upload_mesh(tri_mesh);
auto sun = std::make_shared<DirectionalLight>(Vec3(-1, -1, -0.5f), Vec3(1.0f), 2.0f); scene.add_mesh(tri_mesh);
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); auto sun = std::make_shared<DirectionalLight>(Vec3(-1, -1, -0.5f), Vec3(1.0f), 2.0f);
point->set_cast_shadows(true); sun->set_cast_shadows(true);
scene.add_light(point); scene.add_light(sun);
// Camera auto point = std::make_shared<PointLight>(Vec3(0, 2.5f, 1.5f), Vec3(1.0f, 0.95f, 0.8f), 10.0f, 10.0f);
Camera camera(Vec3(0.0f, 1.8f, 4.5f), Vec3(0.0f, 0.6f, 0.0f)); point->set_cast_shadows(true);
camera.set_perspective(45.0f, static_cast<Real>(fb_w) / static_cast<Real>(fb_h), 0.1f, 200.0f); scene.add_light(point);
// Build BVH from scene meshes Camera camera(Vec3(0.0f, 1.8f, 4.5f), Vec3(0.0f, 0.6f, 0.0f));
std::vector<Triangle> triangles; camera.set_perspective(45.0f, static_cast<Real>(fb_w) / static_cast<Real>(fb_h), 0.1f, 200.0f);
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());
}
BVH bvh; // Geometry cache: single source of truth
if (!bvh.build(triangles)) { GeometryCache geom;
ARE_LOG_CRITICAL("phase5_test: BVH build failed"); if (!geom.build_from_scene(scene)) {
return -1; ARE_LOG_CRITICAL("GeometryCache build failed");
} return -1;
}
// Ray tracer config // Provide triangle base offsets to rasterizer for primitive id output
RayTracingConfig rtc; rasterizer.set_triangle_base_provider([&](size_t mesh_index) {
rtc.backend = RayTracingBackend::ARE_RT_BACKEND_CPU; return geom.get_mesh_triangle_base(mesh_index);
rtc.spp = 1; });
rtc.max_depth = 4;
rtc.enable_gi = false;
rtc.enable_ao = false;
rtc.ao_samples = 1;
rtc.ao_radius = 1.0f;
CPURayTracer tracer(rtc); // CPU ray tracer uses the same BVH (same triangle layout)
tracer.update_bvh(bvh); 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:"); CPURayTracer tracer(rtc);
ARE_LOG_INFO(" 1: spp = 1"); tracer.update_bvh(geom.get_bvh());
ARE_LOG_INFO(" 2: spp = 16");
ARE_LOG_INFO(" A: toggle AO");
ARE_LOG_INFO(" G: toggle GI");
ARE_LOG_INFO(" ESC: exit");
bool request_render = true; bool request_render = true;
while (!window.should_close()) { while (!window.should_close()) {
std::cout << "RENDERING" << std::endl; window.poll_events();
window.poll_events();
if (window.is_key_pressed(256)) { if (window.is_key_pressed(256)) {
window.set_should_close(true); window.set_should_close(true);
} }
if (window.is_key_pressed(49)) { // 1 int new_fb_w = 0, new_fb_h = 0;
rtc.spp = 1; window.get_framebuffer_size(new_fb_w, new_fb_h);
tracer.set_config(rtc); if (new_fb_w <= 0 || new_fb_h <= 0) {
request_render = true; window.swap_buffers();
} continue;
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;
}
// Handle resize if (new_fb_w != fb_w || new_fb_h != fb_h) {
int new_fb_w = 0; fb_w = new_fb_w;
int new_fb_h = 0; fb_h = new_fb_h;
window.get_framebuffer_size(new_fb_w, new_fb_h);
// If minimized / invalid size: keep window responsive, skip rendering rasterizer.resize(fb_w, fb_h);
if (new_fb_w <= 0 || new_fb_h <= 0) { glDeleteTextures(1, &output_tex);
window.swap_buffers(); output_tex = create_output_texture_rgba16f(fb_w, fb_h);
continue; 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) { request_render = true;
fb_w = new_fb_w; }
fb_h = new_fb_h;
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); glBindFramebuffer(GL_FRAMEBUFFER, 0);
output_tex = create_output_texture_rgba16f(fb_w, fb_h); 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) { glBindVertexArray(quad_vao);
ARE_PROFILE_SCOPE("CPU Ray Trace"); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
tracer.render(scene, camera, &gbuffer, output_tex); glBindVertexArray(0);
request_render = false;
}
// Present glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0); window.swap_buffers();
glViewport(0, 0, fb_w, fb_h); }
glDisable(GL_DEPTH_TEST);
display.use(); glDeleteTextures(1, &output_tex);
display.set_uniform("u_tex", 0); glDeleteVertexArrays(1, &quad_vao);
glActiveTexture(GL_TEXTURE0); Profiler::shutdown();
glBindTexture(GL_TEXTURE_2D, output_tex); Logger::shutdown();
return 0;
glBindVertexArray(quad_vao); } catch (const std::exception& e) {
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); Logger::init(LogLevel::ARE_LOG_INFO);
glBindVertexArray(0); ARE_LOG_CRITICAL(std::string("Unhandled exception: ") + e.what());
Logger::shutdown();
glBindTexture(GL_TEXTURE_2D, 0); return -1;
}
window.swap_buffers();
}
glDeleteTextures(1, &output_tex);
glDeleteVertexArrays(1, &quad_vao);
Profiler::print_results();
Profiler::shutdown();
Logger::shutdown();
return 0;
} }

View File

@ -6,12 +6,12 @@
#ifndef ARE_INCLUDE_ACCELERATION_BVH_H #ifndef ARE_INCLUDE_ACCELERATION_BVH_H
#define 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_builder.h>
#include <are/acceleration/bvh_node.h>
#include <are/core/types.h>
#include <are/geometry/triangle.h> #include <are/geometry/triangle.h>
#include <are/raytracer/ray.h>
#include <are/raytracer/hit_record.h> #include <are/raytracer/hit_record.h>
#include <are/raytracer/ray.h>
#include <vector> #include <vector>
namespace are { namespace are {
@ -22,98 +22,112 @@ namespace are {
*/ */
class BVH { class BVH {
public: public:
/** /**
* @brief Constructor * @brief Constructor
*/ */
BVH(); BVH();
/** /**
* @brief Destructor * @brief Destructor
*/ */
~BVH(); ~BVH();
/** /**
* @brief Build BVH from triangle list * @brief Build BVH from triangle list
* @param triangles Triangle list * @param triangles Triangle list
* @param config Build configuration * @param config Build configuration
* @return true if build succeeded * @return true if build succeeded
*/ */
bool build(const std::vector<Triangle>& triangles, bool build(const std::vector<Triangle> &triangles,
const BVHBuildConfig& config = BVHBuildConfig()); const BVHBuildConfig &config = BVHBuildConfig());
/** /**
* @brief Traverse BVH and find closest intersection * @brief Traverse BVH and find closest intersection
* @param ray Ray to trace * @param ray Ray to trace
* @param hit Output hit record * @param hit Output hit record
* @return true if intersection found * @return true if intersection found
*/ */
bool intersect(const Ray& ray, HitRecord& hit) const; bool intersect(const Ray &ray, HitRecord &hit) const;
/** /**
* @brief Fast occlusion test (any hit) * @brief Fast occlusion test (any hit)
* @param ray Ray to trace * @param ray Ray to trace
* @param t_max Maximum t value * @param t_max Maximum t value
* @return true if any intersection found * @return true if any intersection found
*/ */
bool intersect_any(const Ray& ray, Real t_max) const; 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 * @brief Check if BVH is built
* @return true if built * @return true if built
*/ */
bool is_built() const { return !nodes_.empty(); } bool is_built() const {
return !nodes_.empty();
}
/** /**
* @brief Get BVH nodes (for GPU upload) * @brief Get BVH nodes (for GPU upload)
* @return Node array * @return Node array
*/ */
const std::vector<BVHNode>& get_nodes() const { return nodes_; } const std::vector<BVHNode> &get_nodes() const {
return nodes_;
}
/** /**
* @brief Get primitive indices * @brief Get primitive indices
* @return Index array * @return Index array
*/ */
const std::vector<uint32_t>& get_primitive_indices() const { const std::vector<uint32_t> &get_primitive_indices() const {
return primitive_indices_; return primitive_indices_;
} }
/** /**
* @brief Get triangles * @brief Get triangles
* @return Triangle array * @return Triangle array
*/ */
const std::vector<Triangle>& get_triangles() const { return triangles_; } const std::vector<Triangle> &get_triangles() const {
return triangles_;
}
/** /**
* @brief Get memory usage in bytes * @brief Get memory usage in bytes
* @return Memory usage * @return Memory usage
*/ */
size_t get_memory_usage() const; size_t get_memory_usage() const;
/** /**
* @brief Clear BVH data * @brief Clear BVH data
*/ */
void clear(); void clear();
private: private:
// Recursive traversal (kept for reference) // Recursive traversal (kept for reference)
bool intersect_recursive(uint32_t node_index, const Ray& ray, HitRecord& hit) const; 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; 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;
// 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 // Optimized iterative traversal
std::vector<uint32_t> primitive_indices_; ///< Primitive index array bool intersect_iterative(const Ray &ray, HitRecord &hit) const;
std::vector<Triangle> triangles_; ///< Triangle data bool intersect_any_iterative(const Ray &ray, Real t_max) const;
uint32_t root_index_; ///< Root node index
// 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
}; };
} // namespace are } // namespace are

View File

@ -14,8 +14,14 @@ namespace are {
/** /**
* @class GBuffer * @class GBuffer
* @brief G-Buffer for deferred rendering * @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 { class GBuffer {
public: public:
@ -55,7 +61,7 @@ public:
/** /**
* @brief Bind texture for reading * @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 * @param texture_unit Texture unit to bind to
*/ */
void bind_texture(int index, int texture_unit); 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_albedo_texture() const { return albedo_texture_; }
uint32_t get_material_texture() const { return material_texture_; } uint32_t get_material_texture() const { return material_texture_; }
uint32_t get_depth_texture() const { return depth_texture_; } uint32_t get_depth_texture() const { return depth_texture_; }
uint32_t get_primitive_id_texture() const { return primitive_id_texture_; }
// Dimensions // Dimensions
int get_width() const { return width_; } int get_width() const { return width_; }
@ -73,6 +80,15 @@ public:
/** /**
* @brief Read pixel data from G-Buffer * @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 index Buffer index
* @param data Output data pointer * @param data Output data pointer
*/ */
@ -84,17 +100,17 @@ private:
void create_framebuffer(); void create_framebuffer();
uint32_t fbo_; ///< Framebuffer object 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_;
uint32_t position_texture_; ///< World position (RGB16F) uint32_t normal_texture_;
uint32_t normal_texture_; ///< World normal (RGB16F) uint32_t albedo_texture_;
uint32_t albedo_texture_; ///< Albedo + Metallic (RGBA8) uint32_t material_texture_;
uint32_t material_texture_; ///< Roughness + AO (RG8) uint32_t depth_texture_;
uint32_t depth_texture_; ///< Depth (R32F) uint32_t primitive_id_texture_;
int width_; ///< Buffer width int width_;
int height_; ///< Buffer height int height_;
}; };
} // namespace are } // namespace are

View File

@ -7,12 +7,12 @@
#define ARE_INCLUDE_RASTERIZER_RASTERIZER_H #define ARE_INCLUDE_RASTERIZER_RASTERIZER_H
#include <are/core/types.h> #include <are/core/types.h>
#include <are/core/config.h>
#include <memory> #include <memory>
#include <functional>
#include <string>
namespace are { namespace are {
// Forward declarations
class GBuffer; class GBuffer;
class ShaderProgram; class ShaderProgram;
class SceneManager; class SceneManager;
@ -20,67 +20,52 @@ class Camera;
class Mesh; class Mesh;
/** /**
* @class Rasterizer * @struct RasterizerState
* @brief OpenGL rasterization pipeline * @brief Rasterizer fixed-function state (configurable)
*
* Renders scene geometry to G-Buffer using traditional rasterization.
*/ */
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 { class Rasterizer {
public: public:
/**
* @brief Constructor
* @param width Framebuffer width
* @param height Framebuffer height
*/
Rasterizer(int width, int height); Rasterizer(int width, int height);
/**
* @brief Destructor
*/
~Rasterizer(); ~Rasterizer();
/**
* @brief Resize framebuffer
* @param width New width
* @param height New height
*/
void resize(int width, int 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); void render_gbuffer(const SceneManager& scene, const Camera& camera);
/**
* @brief Get G-Buffer
* @return G-Buffer reference
*/
GBuffer& get_gbuffer(); GBuffer& get_gbuffer();
const GBuffer& get_gbuffer() const; const GBuffer& get_gbuffer() const;
/**
* @brief Upload mesh data to GPU
* @param mesh Mesh to upload
*/
void upload_mesh(Mesh& mesh); void upload_mesh(Mesh& mesh);
/**
* @brief Delete mesh GPU resources
* @param mesh Mesh to delete
*/
void delete_mesh(Mesh& mesh); void delete_mesh(Mesh& mesh);
private:
void initialize_shaders(const std::string& shader_dir); 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); void setup_mesh_buffers(Mesh& mesh);
std::unique_ptr<GBuffer> gbuffer_; ///< G-Buffer std::unique_ptr<GBuffer> gbuffer_;
std::unique_ptr<ShaderProgram> gbuffer_shader_; ///< G-Buffer shader std::unique_ptr<ShaderProgram> gbuffer_shader_;
int width_; ///< Framebuffer width std::function<uint32_t(size_t)> triangle_base_provider_;
int height_; ///< Framebuffer height RasterizerState state_;
int width_;
int height_;
}; };
} // namespace are } // namespace are

View File

@ -12,73 +12,28 @@
namespace are { namespace are {
/**
* @enum ShaderType
* @brief Shader stage types
*/
enum class ShaderType { enum class ShaderType {
ARE_SHADER_VERTEX, ARE_SHADER_VERTEX,
ARE_SHADER_FRAGMENT, ARE_SHADER_FRAGMENT,
ARE_SHADER_COMPUTE ARE_SHADER_COMPUTE
}; };
/**
* @class ShaderProgram
* @brief OpenGL shader program management
*/
class ShaderProgram { class ShaderProgram {
public: public:
/**
* @brief Constructor
*/
ShaderProgram(); ShaderProgram();
/**
* @brief Destructor
*/
~ShaderProgram(); ~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); 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); bool compile_shader(ShaderType type, const std::string& source);
/**
* @brief Link shader program
* @return true if linking succeeded
*/
bool link(); bool link();
/**
* @brief Use this shader program
*/
void use() const; void use() const;
/**
* @brief Check if program is valid
* @return true if valid
*/
bool is_valid() const { return program_ != 0 && linked_; } bool is_valid() const { return program_ != 0 && linked_; }
/**
* @brief Get OpenGL program ID
* @return Program ID
*/
uint32_t get_program() const { return program_; } 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, 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, float value);
void set_uniform(const std::string& name, const Vec2& value); void set_uniform(const std::string& name, const Vec2& value);
void set_uniform(const std::string& name, const Vec3& 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 Mat3& value);
void set_uniform(const std::string& name, const Mat4& 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); int get_uniform_location(const std::string& name);
private: private:
bool check_compile_errors(uint32_t shader, ShaderType type); bool check_compile_errors(uint32_t shader, ShaderType type);
bool check_link_errors(); bool check_link_errors();
uint32_t program_; ///< OpenGL program ID uint32_t program_;
uint32_t vertex_shader_; ///< Vertex shader ID uint32_t vertex_shader_;
uint32_t fragment_shader_; ///< Fragment shader ID uint32_t fragment_shader_;
uint32_t compute_shader_; ///< Compute shader ID uint32_t compute_shader_;
bool linked_;
bool linked_; ///< Link status std::unordered_map<std::string, int> uniform_cache_;
std::unordered_map<std::string, int> uniform_cache_; ///< Uniform location cache
}; };
} // namespace are } // namespace are

View File

@ -89,7 +89,7 @@ private:
* @param max_distance Maximum distance * @param max_distance Maximum distance
* @return true if in shadow * @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 BVH* bvh_; ///< BVH reference
const SceneManager* scene_; ///< Scene reference const SceneManager* scene_; ///< Scene reference

View File

@ -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

View File

@ -1,33 +1,26 @@
#version 430 core #version 430 core
// Inputs from vertex shader
in vec3 v_world_position; in vec3 v_world_position;
in vec3 v_world_normal; in vec3 v_world_normal;
in vec2 v_texcoord; in vec2 v_texcoord;
in vec3 v_world_tangent; flat in uint v_triangle_id_base;
// Material uniforms
uniform vec3 u_albedo; uniform vec3 u_albedo;
uniform float u_metallic; uniform float u_metallic;
uniform float u_roughness; uniform float u_roughness;
// G-Buffer outputs
layout(location = 0) out vec3 g_position; layout(location = 0) out vec3 g_position;
layout(location = 1) out vec3 g_normal; layout(location = 1) out vec3 g_normal;
layout(location = 2) out vec4 g_albedo_metallic; layout(location = 2) out vec4 g_albedo_metallic;
layout(location = 3) out vec2 g_roughness_ao; layout(location = 3) out vec2 g_roughness_ao;
layout(location = 4) out uint g_primitive_id;
void main() { void main() {
// Output world position
g_position = v_world_position; g_position = v_world_position;
// Output normalized world normal
g_normal = normalize(v_world_normal); g_normal = normalize(v_world_normal);
// Output albedo (RGB) and metallic (A)
g_albedo_metallic = vec4(u_albedo, u_metallic); 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); 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);
} }

View File

@ -1,35 +1,29 @@
#version 430 core #version 430 core
// Vertex attributes
layout(location = 0) in vec3 a_position; layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_normal; layout(location = 1) in vec3 a_normal;
layout(location = 2) in vec2 a_texcoord; layout(location = 2) in vec2 a_texcoord;
layout(location = 3) in vec3 a_tangent; layout(location = 3) in vec3 a_tangent;
// Uniforms
uniform mat4 u_model; uniform mat4 u_model;
uniform mat4 u_view; uniform mat4 u_view;
uniform mat4 u_projection; uniform mat4 u_projection;
uniform mat3 u_normal_matrix; 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_position;
out vec3 v_world_normal; out vec3 v_world_normal;
out vec2 v_texcoord; out vec2 v_texcoord;
out vec3 v_world_tangent; flat out uint v_triangle_id_base;
void main() { void main() {
// Transform position to world space
vec4 world_pos = u_model * vec4(a_position, 1.0); vec4 world_pos = u_model * vec4(a_position, 1.0);
v_world_position = world_pos.xyz; v_world_position = world_pos.xyz;
// Transform normal and tangent to world space
v_world_normal = normalize(u_normal_matrix * a_normal); 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_texcoord = a_texcoord;
v_triangle_id_base = u_triangle_id_base;
// Transform to clip space
gl_Position = u_projection * u_view * world_pos; gl_Position = u_projection * u_view * world_pos;
} }

View File

@ -3,6 +3,7 @@
* @brief Implementation of BVH class (optimized version) * @brief Implementation of BVH class (optimized version)
*/ */
#include <stack>
#include <algorithm> #include <algorithm>
#include <are/acceleration/bvh.h> #include <are/acceleration/bvh.h>
#include <are/core/logger.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); 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 BVH::get_memory_usage() const {
size_t total = 0; size_t total = 0;
total += nodes_.size() * sizeof(BVHNode); total += nodes_.size() * sizeof(BVHNode);

View File

@ -18,47 +18,52 @@ GBuffer::GBuffer(int width, int height)
, albedo_texture_(0) , albedo_texture_(0)
, material_texture_(0) , material_texture_(0)
, depth_texture_(0) , depth_texture_(0)
, primitive_id_texture_(0)
, width_(width) , width_(width)
, height_(height) { , height_(height) {
create_textures(); create_textures();
create_framebuffer(); create_framebuffer();
ARE_LOG_INFO("GBuffer: Created " + std::to_string(width) + "x" + std::to_string(height));
} }
GBuffer::~GBuffer() { GBuffer::~GBuffer() {
delete_textures(); delete_textures();
if (rbo_depth_ != 0) { if (rbo_depth_ != 0) {
glDeleteRenderbuffers(1, &rbo_depth_); glDeleteRenderbuffers(1, &rbo_depth_);
rbo_depth_ = 0;
} }
if (fbo_ != 0) { if (fbo_ != 0) {
glDeleteFramebuffers(1, &fbo_); glDeleteFramebuffers(1, &fbo_);
fbo_ = 0;
} }
} }
void GBuffer::resize(int width, int height) { void GBuffer::resize(int width, int height) {
ARE_PROFILE_FUNCTION(); ARE_PROFILE_FUNCTION();
if (width == width_ && height == height_) { if (width == width_ && height == height_) {
return; return;
} }
width_ = width; width_ = width;
height_ = height; height_ = height;
// Recreate textures and framebuffer
delete_textures(); delete_textures();
if (rbo_depth_ != 0) { if (rbo_depth_ != 0) {
glDeleteRenderbuffers(1, &rbo_depth_); glDeleteRenderbuffers(1, &rbo_depth_);
rbo_depth_ = 0; rbo_depth_ = 0;
} }
if (fbo_ != 0) {
glDeleteFramebuffers(1, &fbo_);
fbo_ = 0;
}
create_textures(); create_textures();
create_framebuffer(); create_framebuffer();
ARE_LOG_INFO("GBuffer: Resized to " + std::to_string(width) + "x" + std::to_string(height));
} }
void GBuffer::bind() { void GBuffer::bind() {
@ -78,13 +83,14 @@ void GBuffer::clear() {
void GBuffer::bind_texture(int index, int texture_unit) { void GBuffer::bind_texture(int index, int texture_unit) {
glActiveTexture(GL_TEXTURE0 + texture_unit); glActiveTexture(GL_TEXTURE0 + texture_unit);
switch (index) { switch (index) {
case 0: glBindTexture(GL_TEXTURE_2D, position_texture_); break; case 0: glBindTexture(GL_TEXTURE_2D, position_texture_); break;
case 1: glBindTexture(GL_TEXTURE_2D, normal_texture_); break; case 1: glBindTexture(GL_TEXTURE_2D, normal_texture_); break;
case 2: glBindTexture(GL_TEXTURE_2D, albedo_texture_); break; case 2: glBindTexture(GL_TEXTURE_2D, albedo_texture_); break;
case 3: glBindTexture(GL_TEXTURE_2D, material_texture_); break; case 3: glBindTexture(GL_TEXTURE_2D, material_texture_); break;
case 4: glBindTexture(GL_TEXTURE_2D, depth_texture_); break; case 4: glBindTexture(GL_TEXTURE_2D, depth_texture_); break;
case 5: glBindTexture(GL_TEXTURE_2D, primitive_id_texture_); break;
default: default:
ARE_LOG_WARN("GBuffer: Invalid texture index " + std::to_string(index)); ARE_LOG_WARN("GBuffer: Invalid texture index " + std::to_string(index));
break; break;
@ -93,55 +99,61 @@ void GBuffer::bind_texture(int index, int texture_unit) {
void GBuffer::read_pixels(int index, void* data) { void GBuffer::read_pixels(int index, void* data) {
ARE_PROFILE_FUNCTION(); ARE_PROFILE_FUNCTION();
bind(); // Robust: read from texture object (not from FBO read buffer)
glPixelStorei(GL_PACK_ALIGNMENT, 1);
GLenum attachment; glPixelStorei(GL_PACK_ROW_LENGTH, 0);
GLenum format; glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
GLenum type; glPixelStorei(GL_PACK_SKIP_ROWS, 0);
uint32_t tex = 0;
GLenum format = GL_RGBA;
GLenum type = GL_UNSIGNED_BYTE;
switch (index) { switch (index) {
case 0: // Position case 0:
attachment = GL_COLOR_ATTACHMENT0; tex = position_texture_;
format = GL_RGB; format = GL_RGB;
type = GL_FLOAT; type = GL_FLOAT;
break; break;
case 1: // Normal case 1:
attachment = GL_COLOR_ATTACHMENT1; tex = normal_texture_;
format = GL_RGB; format = GL_RGB;
type = GL_FLOAT; type = GL_FLOAT;
break; break;
case 2: // Albedo case 2:
attachment = GL_COLOR_ATTACHMENT2; tex = albedo_texture_;
format = GL_RGBA; format = GL_RGBA;
type = GL_UNSIGNED_BYTE; type = GL_UNSIGNED_BYTE;
break; break;
case 3: // Material case 3:
attachment = GL_COLOR_ATTACHMENT3; tex = material_texture_;
format = GL_RG; format = GL_RG;
type = GL_UNSIGNED_BYTE; type = GL_UNSIGNED_BYTE;
break; break;
case 4: // Depth case 4:
attachment = GL_DEPTH_ATTACHMENT; tex = depth_texture_;
format = GL_DEPTH_COMPONENT; format = GL_DEPTH_COMPONENT;
type = GL_FLOAT; type = GL_FLOAT;
break; break;
case 5:
tex = primitive_id_texture_;
format = GL_RED_INTEGER;
type = GL_UNSIGNED_INT;
break;
default: default:
ARE_LOG_ERROR("GBuffer: Invalid buffer index for read_pixels"); ARE_LOG_ERROR("GBuffer: Invalid buffer index for read_pixels");
unbind();
return; return;
} }
glReadBuffer(attachment); glBindTexture(GL_TEXTURE_2D, tex);
glReadPixels(0, 0, width_, height_, format, type, data); glGetTexImage(GL_TEXTURE_2D, 0, format, type, data);
glBindTexture(GL_TEXTURE_2D, 0);
unbind();
} }
void GBuffer::create_textures() { void GBuffer::create_textures() {
ARE_PROFILE_FUNCTION(); ARE_PROFILE_FUNCTION();
// Position texture (RGB16F)
glGenTextures(1, &position_texture_); glGenTextures(1, &position_texture_);
glBindTexture(GL_TEXTURE_2D, position_texture_); glBindTexture(GL_TEXTURE_2D, position_texture_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width_, height_, 0, GL_RGB, GL_FLOAT, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width_, height_, 0, GL_RGB, GL_FLOAT, nullptr);
@ -149,8 +161,7 @@ void GBuffer::create_textures() {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Normal texture (RGB16F)
glGenTextures(1, &normal_texture_); glGenTextures(1, &normal_texture_);
glBindTexture(GL_TEXTURE_2D, normal_texture_); glBindTexture(GL_TEXTURE_2D, normal_texture_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width_, height_, 0, GL_RGB, GL_FLOAT, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width_, height_, 0, GL_RGB, GL_FLOAT, nullptr);
@ -158,8 +169,7 @@ void GBuffer::create_textures() {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Albedo + Metallic texture (RGBA8)
glGenTextures(1, &albedo_texture_); glGenTextures(1, &albedo_texture_);
glBindTexture(GL_TEXTURE_2D, albedo_texture_); glBindTexture(GL_TEXTURE_2D, albedo_texture_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width_, height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width_, height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
@ -167,8 +177,7 @@ void GBuffer::create_textures() {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Roughness + AO texture (RG8)
glGenTextures(1, &material_texture_); glGenTextures(1, &material_texture_);
glBindTexture(GL_TEXTURE_2D, material_texture_); glBindTexture(GL_TEXTURE_2D, material_texture_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width_, height_, 0, GL_RG, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width_, height_, 0, GL_RG, GL_UNSIGNED_BYTE, nullptr);
@ -176,76 +185,70 @@ void GBuffer::create_textures() {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Depth texture (R32F)
glGenTextures(1, &depth_texture_); glGenTextures(1, &depth_texture_);
glBindTexture(GL_TEXTURE_2D, 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_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }
void GBuffer::delete_textures() { void GBuffer::delete_textures() {
if (position_texture_ != 0) { if (position_texture_ != 0) glDeleteTextures(1, &position_texture_);
glDeleteTextures(1, &position_texture_); if (normal_texture_ != 0) glDeleteTextures(1, &normal_texture_);
position_texture_ = 0; if (albedo_texture_ != 0) glDeleteTextures(1, &albedo_texture_);
} if (material_texture_ != 0) glDeleteTextures(1, &material_texture_);
if (normal_texture_ != 0) { if (depth_texture_ != 0) glDeleteTextures(1, &depth_texture_);
glDeleteTextures(1, &normal_texture_); if (primitive_id_texture_ != 0) glDeleteTextures(1, &primitive_id_texture_);
normal_texture_ = 0;
} position_texture_ = 0;
if (albedo_texture_ != 0) { normal_texture_ = 0;
glDeleteTextures(1, &albedo_texture_); albedo_texture_ = 0;
albedo_texture_ = 0; material_texture_ = 0;
} depth_texture_ = 0;
if (material_texture_ != 0) { primitive_id_texture_ = 0;
glDeleteTextures(1, &material_texture_);
material_texture_ = 0;
}
if (depth_texture_ != 0) {
glDeleteTextures(1, &depth_texture_);
depth_texture_ = 0;
}
} }
void GBuffer::create_framebuffer() { void GBuffer::create_framebuffer() {
ARE_PROFILE_FUNCTION(); ARE_PROFILE_FUNCTION();
// Create framebuffer
glGenFramebuffers(1, &fbo_); glGenFramebuffers(1, &fbo_);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_); glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
// Attach textures
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, position_texture_, 0); 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_ATTACHMENT1, GL_TEXTURE_2D, normal_texture_, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, albedo_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_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[] = { GLenum draw_buffers[] = {
GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT0,
GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT2,
GL_COLOR_ATTACHMENT3 GL_COLOR_ATTACHMENT3,
GL_COLOR_ATTACHMENT4
}; };
glDrawBuffers(4, draw_buffers); glDrawBuffers(5, draw_buffers);
// Create depth renderbuffer glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture_, 0);
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_);
// Check framebuffer completeness
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) { 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); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }

View File

@ -14,172 +14,131 @@
#include <are/core/logger.h> #include <are/core/logger.h>
#include <are/core/profiler.h> #include <are/core/profiler.h>
#include <are/platform/gl_context.h> #include <are/platform/gl_context.h>
#include <glad/glad.h> #include <glad/glad.h>
#include <glm/gtc/matrix_inverse.hpp> #include <glm/gtc/matrix_inverse.hpp>
namespace are { namespace are {
Rasterizer::Rasterizer(int width, int height) 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) { , 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() { Rasterizer::~Rasterizer() = default;
ARE_LOG_INFO("Rasterizer: Destroyed");
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) { void Rasterizer::resize(int width, int height) {
ARE_PROFILE_FUNCTION(); ARE_PROFILE_FUNCTION();
if (width == width_ && height == height_) return;
if (width == width_ && height == height_) {
return;
}
width_ = width; width_ = width;
height_ = height; height_ = height;
gbuffer_->resize(width_, height_);
if (gbuffer_) {
gbuffer_->resize(width, height);
}
ARE_LOG_INFO("Rasterizer: Resized to " + std::to_string(width) + "x" + std::to_string(height));
} }
void Rasterizer::render_gbuffer(const SceneManager& scene, const Camera& camera) { void Rasterizer::render_gbuffer(const SceneManager& scene, const Camera& camera) {
ARE_PROFILE_FUNCTION(); ARE_PROFILE_FUNCTION();
if (!gbuffer_shader_ || !gbuffer_shader_->is_valid()) { 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; return;
} }
// Bind G-Buffer for rendering
gbuffer_->bind(); gbuffer_->bind();
// Clear buffers glClearColor(0, 0, 0, 0);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Enable depth testing if (state_.enable_depth_test) {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS); glDepthFunc(GL_LESS);
} else {
// Enable face culling glDisable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE); }
glCullFace(GL_BACK);
glFrontFace(GL_CCW); if (state_.enable_cull_face) {
glEnable(GL_CULL_FACE);
// Use G-Buffer shader glCullFace(static_cast<GLenum>(state_.cull_face_mode));
glFrontFace(static_cast<GLenum>(state_.front_face));
} else {
glDisable(GL_CULL_FACE);
}
gbuffer_shader_->use(); gbuffer_shader_->use();
// Set view and projection matrices
gbuffer_shader_->set_uniform("u_view", camera.get_view_matrix()); gbuffer_shader_->set_uniform("u_view", camera.get_view_matrix());
gbuffer_shader_->set_uniform("u_projection", camera.get_projection_matrix()); gbuffer_shader_->set_uniform("u_projection", camera.get_projection_matrix());
// Render all meshes
const auto& meshes = scene.get_all_meshes(); const auto& meshes = scene.get_all_meshes();
const auto& materials = scene.get_all_materials();
for (size_t mi = 0; mi < meshes.size(); ++mi) {
for (const auto& mesh : meshes) { const auto& mesh = meshes[mi];
if (mesh.is_empty() || !mesh.has_gpu_resources()) { if (mesh.is_empty() || !mesh.has_gpu_resources()) continue;
continue;
} Mat4 model = Mat4(1.0f);
gbuffer_shader_->set_uniform("u_model", model);
// Set model matrix (identity for now, can be extended with Transform)
Mat4 model_matrix = Mat4(1.0f); Mat3 normal_matrix = glm::inverseTranspose(Mat3(model));
gbuffer_shader_->set_uniform("u_model", model_matrix);
// Calculate normal matrix
Mat3 normal_matrix = glm::transpose(glm::inverse(Mat3(model_matrix)));
gbuffer_shader_->set_uniform("u_normal_matrix", normal_matrix); gbuffer_shader_->set_uniform("u_normal_matrix", normal_matrix);
// Set material properties uint32_t tri_base = triangle_base_provider_ ? triangle_base_provider_(mi) : 0u;
MaterialHandle mat_handle = mesh.get_material(); // IMPORTANT: u_triangle_id_base is uint in GLSL, must use glUniform1ui
if (mat_handle != are_invalid_handle && mat_handle <= materials.size()) { gbuffer_shader_->set_uniform("u_triangle_id_base", tri_base);
const Material& material = materials[mat_handle - 1]; // Handle is 1-based
gbuffer_shader_->set_uniform("u_albedo", material.get_albedo()); const Material* mat = scene.get_material(mesh.get_material());
gbuffer_shader_->set_uniform("u_metallic", material.get_metallic()); if (mat) {
gbuffer_shader_->set_uniform("u_roughness", material.get_roughness()); 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 { } else {
// Default material gbuffer_shader_->set_uniform("u_albedo", Vec3(0.8f));
gbuffer_shader_->set_uniform("u_albedo", Vec3(0.8f, 0.8f, 0.8f));
gbuffer_shader_->set_uniform("u_metallic", 0.0f); gbuffer_shader_->set_uniform("u_metallic", 0.0f);
gbuffer_shader_->set_uniform("u_roughness", 0.5f); gbuffer_shader_->set_uniform("u_roughness", 0.5f);
} }
// Draw mesh
glBindVertexArray(mesh.get_vao()); glBindVertexArray(mesh.get_vao());
glDrawElements(GL_TRIANGLES, glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mesh.get_index_count()), GL_UNSIGNED_INT, nullptr);
static_cast<GLsizei>(mesh.get_index_count()),
GL_UNSIGNED_INT,
nullptr);
glBindVertexArray(0); glBindVertexArray(0);
} }
// Disable states
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
// Unbind G-Buffer
gbuffer_->unbind(); gbuffer_->unbind();
ARE_GL_CHECK(); ARE_GL_CHECK();
} }
GBuffer& Rasterizer::get_gbuffer() { GBuffer& Rasterizer::get_gbuffer() { return *gbuffer_; }
return *gbuffer_; const GBuffer& Rasterizer::get_gbuffer() const { return *gbuffer_; }
}
const GBuffer& Rasterizer::get_gbuffer() const {
return *gbuffer_;
}
void Rasterizer::upload_mesh(Mesh& mesh) { void Rasterizer::upload_mesh(Mesh& mesh) {
ARE_PROFILE_FUNCTION(); ARE_PROFILE_FUNCTION();
if (mesh.is_empty()) { if (mesh.is_empty()) {
ARE_LOG_WARN("Rasterizer: Attempting to upload empty mesh"); ARE_LOG_WARN("Rasterizer: upload_mesh on empty mesh");
return; return;
} }
if (mesh.has_gpu_resources()) delete_mesh(mesh);
// Delete existing GPU resources if any
if (mesh.has_gpu_resources()) {
delete_mesh(mesh);
}
setup_mesh_buffers(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) { void Rasterizer::delete_mesh(Mesh& mesh) {
ARE_PROFILE_FUNCTION(); ARE_PROFILE_FUNCTION();
uint32_t vao = mesh.get_vao(); uint32_t vao = mesh.get_vao();
uint32_t vbo = mesh.get_vbo(); uint32_t vbo = mesh.get_vbo();
uint32_t ebo = mesh.get_ebo(); uint32_t ebo = mesh.get_ebo();
if (vao != 0) { if (vao) glDeleteVertexArrays(1, &vao);
glDeleteVertexArrays(1, &vao); if (vbo) glDeleteBuffers(1, &vbo);
} if (ebo) glDeleteBuffers(1, &ebo);
if (vbo != 0) {
glDeleteBuffers(1, &vbo);
}
if (ebo != 0) {
glDeleteBuffers(1, &ebo);
}
mesh.set_vao(0); mesh.set_vao(0);
mesh.set_vbo(0); mesh.set_vbo(0);
mesh.set_ebo(0); mesh.set_ebo(0);
@ -187,94 +146,51 @@ void Rasterizer::delete_mesh(Mesh& mesh) {
void Rasterizer::initialize_shaders(const std::string& shader_dir) { void Rasterizer::initialize_shaders(const std::string& shader_dir) {
ARE_PROFILE_FUNCTION(); ARE_PROFILE_FUNCTION();
if (!gbuffer_shader_) { bool ok = true;
gbuffer_shader_ = std::make_unique<ShaderProgram>(); 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"; if (!ok) {
ARE_LOG_ERROR("Rasterizer: Failed to init gbuffer shaders");
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");
} }
} }
void Rasterizer::setup_mesh_buffers(Mesh& mesh) { void Rasterizer::setup_mesh_buffers(Mesh& mesh) {
ARE_PROFILE_FUNCTION(); ARE_PROFILE_FUNCTION();
uint32_t vao, vbo, ebo; uint32_t vao = 0, vbo = 0, ebo = 0;
// Create VAO
glGenVertexArrays(1, &vao); glGenVertexArrays(1, &vao);
glBindVertexArray(vao); glBindVertexArray(vao);
// Create VBO
glGenBuffers(1, &vbo); glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, glBufferData(GL_ARRAY_BUFFER, mesh.get_vertex_count() * sizeof(Vertex), mesh.get_vertices().data(), GL_STATIC_DRAW);
mesh.get_vertex_count() * sizeof(Vertex),
mesh.get_vertices().data(),
GL_STATIC_DRAW);
// Create EBO
glGenBuffers(1, &ebo); glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.get_index_count() * sizeof(uint32_t), mesh.get_indices().data(), GL_STATIC_DRAW);
mesh.get_index_count() * sizeof(uint32_t),
mesh.get_indices().data(),
GL_STATIC_DRAW);
// Setup vertex attributes
// Position (location = 0)
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
sizeof(Vertex),
reinterpret_cast<void*>(get_position_offset())); reinterpret_cast<void*>(get_position_offset()));
// Normal (location = 1)
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
sizeof(Vertex),
reinterpret_cast<void*>(get_normal_offset())); reinterpret_cast<void*>(get_normal_offset()));
// Texcoord (location = 2)
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
sizeof(Vertex),
reinterpret_cast<void*>(get_texcoord_offset())); reinterpret_cast<void*>(get_texcoord_offset()));
// Tangent (location = 3)
glEnableVertexAttribArray(3); glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
sizeof(Vertex),
reinterpret_cast<void*>(get_tangent_offset())); reinterpret_cast<void*>(get_tangent_offset()));
// Unbind VAO
glBindVertexArray(0); glBindVertexArray(0);
// Store handles in mesh
mesh.set_vao(vao); mesh.set_vao(vao);
mesh.set_vbo(vbo); mesh.set_vbo(vbo);
mesh.set_ebo(ebo); mesh.set_ebo(ebo);
ARE_GL_CHECK(); ARE_GL_CHECK();
} }

View File

@ -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) { 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) { void ShaderProgram::set_uniform(const std::string& name, float value) {

View File

@ -1,14 +1,12 @@
/** /**
* @file cpu_raytracer.cpp * @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/raytracer/cpu_raytracer.h>
#include <are/acceleration/bvh.h> #include <are/acceleration/bvh.h>
#include <are/scene/scene_manager.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/light.h>
#include <are/scene/directional_light.h> #include <are/scene/directional_light.h>
#include <are/scene/point_light.h> #include <are/scene/point_light.h>
@ -26,34 +24,37 @@
#include <algorithm> #include <algorithm>
#include <limits> #include <limits>
#include <stdexcept> #include <stdexcept>
#include <vector>
// #ifdef ARE_USE_OPENMP
// #include <omp.h>
// #endif
namespace are { namespace are {
namespace { namespace {
/** inline Real compute_ray_epsilon(const Vec3& p) {
* @brief Apply simple Reinhard tonemapping. Real s = std::max({std::abs(p.x), std::abs(p.y), std::abs(p.z), 1.0f});
* @param c HDR color return 1e-4f * s;
* @param exposure Exposure value }
* @return LDR color in [0,1]
*/ 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) { inline Vec3 tonemap_reinhard(const Vec3& c, Real exposure) {
Vec3 x = c * exposure; Vec3 x = c * exposure;
return x / (Vec3(1.0f) + x); return x / (Vec3(1.0f) + x);
} }
/** inline Vec3 decode_albedo_from_rgba8(uint8_t r, uint8_t g, uint8_t b) {
* @brief Offset ray origin to reduce self-intersection. return Vec3(r, g, b) / 255.0f;
* @param p Hit position }
* @param n Shading normal
* @return Offset position inline Real decode_01_from_u8(uint8_t v) {
*/ return static_cast<Real>(v) / 255.0f;
inline Vec3 offset_ray_origin(const Vec3& p, const Vec3& n) { }
return p + n * (are_epsilon * 10.0f);
inline bool finite_vec3(const Vec3& v) {
return std::isfinite(v.x) && std::isfinite(v.y) && std::isfinite(v.z);
} }
} // namespace } // namespace
@ -78,6 +79,7 @@ void CPURayTracer::render(const SceneManager& scene,
const GBuffer* gbuffer, const GBuffer* gbuffer,
uint32_t output_texture) { uint32_t output_texture) {
ARE_PROFILE_FUNCTION(); ARE_PROFILE_FUNCTION();
(void)camera;
if (!bvh_ || !bvh_->is_built()) { if (!bvh_ || !bvh_->is_built()) {
ARE_LOG_ERROR("CPURayTracer: BVH is null or not built"); ARE_LOG_ERROR("CPURayTracer: BVH is null or not built");
@ -85,8 +87,8 @@ void CPURayTracer::render(const SceneManager& scene,
} }
if (!gbuffer) { if (!gbuffer) {
ARE_LOG_CRITICAL("CPURayTracer: GBuffer is null, cannot infer render resolution"); ARE_LOG_CRITICAL("CPURayTracer: GBuffer is null (hybrid requires it)");
throw std::runtime_error("CPURayTracer requires a valid GBuffer for resolution"); throw std::runtime_error("CPURayTracer requires GBuffer in hybrid mode");
} }
if (output_texture == 0) { if (output_texture == 0) {
@ -99,104 +101,142 @@ void CPURayTracer::render(const SceneManager& scene,
height_ = gbuffer->get_height(); height_ = gbuffer->get_height();
if (width_ <= 0 || height_ <= 0) { if (width_ <= 0 || height_ <= 0) {
ARE_LOG_ERROR("CPURayTracer: Invalid render resolution"); ARE_LOG_ERROR("CPURayTracer: Invalid resolution");
return; 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 spp = std::max(1, config_.spp);
const int max_depth = std::max(1, config_.max_depth); 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) { for (int y = 0; y < height_; ++y) {
RandomGenerator& rng = get_thread_random(); RandomGenerator& rng = get_thread_random();
for (int x = 0; x < width_; ++x) { 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) { // Depth validity
Real u = (static_cast<Real>(x) + rng.random_float()) / static_cast<Real>(width_); if (!(depth[idx] > 0.0f && depth[idx] < 0.999999f)) {
Real v = (static_cast<Real>(y) + rng.random_float()) / static_cast<Real>(height_); framebuffer_[idx] = Vec3(0.0f);
continue;
Vec3 origin;
Vec3 direction;
camera.generate_ray(u, v, origin, direction);
Ray ray(origin, direction, are_epsilon, 1e30f);
hdr += trace_ray(ray, max_depth);
} }
hdr /= static_cast<Real>(spp); Vec3 P = pos[idx];
Vec3 Ns = glm::normalize(nrm[idx]);
// Phase 5: tonemap in tracer for standalone output if (!finite_vec3(P) || !finite_vec3(Ns) || glm::length(Ns) < 0.1f) {
Vec3 ldr = tonemap_reinhard(hdr, 1.0f); framebuffer_[idx] = Vec3(0.0f);
framebuffer_[static_cast<size_t>(y * width_ + x)] = Vec4(ldr, 1.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); glBindTexture(GL_TEXTURE_2D, output_texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 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); glBindTexture(GL_TEXTURE_2D, 0);
} }
Vec3 CPURayTracer::trace_ray(const Ray& ray, int depth) { Vec3 CPURayTracer::trace_ray(const Ray& ray, int depth) {
ARE_PROFILE_FUNCTION();
if (depth <= 0) { if (depth <= 0) {
return Vec3(0.0f); return Vec3(0.0f);
} }
HitRecord hit; HitRecord hit;
if (!bvh_->intersect(ray, hit)) { if (!bvh_->intersect(ray, hit)) {
// Simple sky return Vec3(0.0f);
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 shade(hit, ray, depth); Vec3 Ng = hit.normal_;
} if (hit.triangle_index_ < bvh_->get_triangles().size()) {
Ng = bvh_->get_triangles()[hit.triangle_index_].normal();
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 direct = compute_direct_lighting(hit); RandomGenerator& rng = get_thread_random();
Real ao = 1.0f; Vec3 dir = rng.random_cosine_direction(hit.normal_);
if (config_.enable_ao) { Vec3 origin = offset_ray_origin(hit.position_, Ng);
ao = compute_ambient_occlusion(hit); Real eps = compute_ray_epsilon(hit.position_);
}
// Direct term (Lambert) Ray bounce(origin, dir, eps * 4.0f, 1e30f);
Vec3 Lo = albedo * direct * ao; return trace_ray(bounce, depth - 1);
// 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;
} }
Vec3 CPURayTracer::compute_direct_lighting(const HitRecord& hit) { Vec3 CPURayTracer::compute_direct_lighting(const HitRecord& hit) {
ARE_PROFILE_FUNCTION();
Vec3 lighting(0.0f); Vec3 lighting(0.0f);
if (!scene_) { if (!scene_) {
return lighting; return lighting;
@ -204,15 +244,13 @@ Vec3 CPURayTracer::compute_direct_lighting(const HitRecord& hit) {
const auto& lights = scene_->get_all_lights(); const auto& lights = scene_->get_all_lights();
for (const auto& light_ptr : lights) { for (const auto& light_ptr : lights) {
if (!light_ptr) { if (!light_ptr) continue;
continue;
}
Vec3 L(0.0f); Vec3 L(0.0f);
Real max_distance = 1e30f; Real max_distance = 1e30f;
Real attenuation = 1.0f; Real attenuation = 1.0f;
const LightType type = light_ptr->get_type(); LightType type = light_ptr->get_type();
if (type == LightType::ARE_LIGHT_DIRECTIONAL) { if (type == LightType::ARE_LIGHT_DIRECTIONAL) {
const auto* dl = static_cast<const DirectionalLight*>(light_ptr.get()); 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()); const auto* pl = static_cast<const PointLight*>(light_ptr.get());
Vec3 to_light = pl->get_position() - hit.position_; Vec3 to_light = pl->get_position() - hit.position_;
Real dist = glm::length(to_light); Real dist = glm::length(to_light);
if (dist < are_epsilon) { if (dist < are_epsilon) continue;
continue; if (!pl->affects_point(hit.position_)) continue;
}
if (!pl->affects_point(hit.position_)) {
continue;
}
L = to_light / dist; L = to_light / dist;
max_distance = dist; max_distance = dist;
attenuation = pl->calculate_attenuation(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()); const auto* sl = static_cast<const SpotLight*>(light_ptr.get());
Vec3 to_light = sl->get_position() - hit.position_; Vec3 to_light = sl->get_position() - hit.position_;
Real dist = glm::length(to_light); Real dist = glm::length(to_light);
if (dist < are_epsilon) { if (dist < are_epsilon) continue;
continue; if (!sl->affects_point(hit.position_)) continue;
}
if (!sl->affects_point(hit.position_)) {
continue;
}
L = to_light / dist; L = to_light / dist;
max_distance = dist; max_distance = dist;
Vec3 light_to_point = glm::normalize(hit.position_ - sl->get_position()); Vec3 light_to_point = glm::normalize(hit.position_ - sl->get_position());
Real spot = sl->calculate_spot_factor(light_to_point); attenuation *= sl->calculate_spot_factor(light_to_point);
attenuation *= spot;
} else { } else {
continue; continue;
} }
if (light_ptr->get_cast_shadows()) { 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; continue;
} }
} }
Real n_dot_l = std::max(0.0f, glm::dot(hit.normal_, L)); Real n_dot_l = std::max(0.0f, glm::dot(hit.normal_, L));
if (n_dot_l <= 0.0f) { if (n_dot_l <= 0.0f) continue;
continue;
}
Vec3 radiance = light_ptr->get_color() * light_ptr->get_intensity(); Vec3 radiance = light_ptr->get_color() * light_ptr->get_intensity();
lighting += radiance * n_dot_l * attenuation; 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) { Real CPURayTracer::compute_ambient_occlusion(const HitRecord& hit) {
ARE_PROFILE_FUNCTION();
if (!bvh_) { if (!bvh_) {
return 1.0f; return 1.0f;
} }
@ -283,8 +310,9 @@ Real CPURayTracer::compute_ambient_occlusion(const HitRecord& hit) {
int occluded = 0; int occluded = 0;
for (int i = 0; i < ao_samples; ++i) { for (int i = 0; i < ao_samples; ++i) {
Vec3 dir = rng.random_in_hemisphere(hit.normal_); 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)) { if (bvh_->intersect_any(ao_ray, radius)) {
occluded++; occluded++;
} }
@ -294,16 +322,13 @@ Real CPURayTracer::compute_ambient_occlusion(const HitRecord& hit) {
return 1.0f - occ; return 1.0f - occ;
} }
bool CPURayTracer::is_in_shadow(const Vec3& origin, const Vec3& direction, Real max_distance) { bool CPURayTracer::is_in_shadow(const Vec3& origin, const Vec3& direction, Real max_distance, uint32_t ignore_triangle) {
ARE_PROFILE_FUNCTION(); if (!bvh_) return false;
if (!bvh_) {
return false;
}
Real t_max = (max_distance > 0.0f) ? max_distance : 1e30f; Real t_max = (max_distance > 0.0f) ? max_distance : 1e30f;
Ray shadow_ray(origin, direction, are_epsilon, t_max); Real eps = compute_ray_epsilon(origin);
return bvh_->intersect_any(shadow_ray, t_max); Ray shadow(origin, direction, eps * 4.0f, t_max);
return bvh_->intersect_any(shadow, t_max, ignore_triangle);
} }
} // namespace are } // namespace are

View File

@ -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