这个架构最后一次commit
parent
01dd5bd91e
commit
96ffcd4edc
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
#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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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