Refractor&Add: 使用shared_ptr优化Shader管理、添加基于G-Buffer的光线追踪、添加场景重建API

master
ternaryop8479 2026-02-11 00:27:37 +08:00
parent 52c6ace2cd
commit b8ae9808a8
12 changed files with 457 additions and 364 deletions

Binary file not shown.

View File

@ -442,7 +442,7 @@ int main() {
RendererConfig config;
config.width_ = WINDOW_WIDTH;
config.height_ = WINDOW_HEIGHT;
config.samples_per_pixel_ = 1;
config.samples_per_pixel_ = 4;
config.max_ray_depth_ = 4;
config.enable_accumulation_ = false;
config.enable_denoising_ = false;

View File

@ -7,6 +7,7 @@
#include "resource/buffer.h"
#include "resource/shader.h"
#include "scene/scene.h"
#include <memory>
namespace are {
@ -70,14 +71,14 @@ public:
/// @brief Set compute shader (called by renderer)
/// @param shader Compute shader
void set_compute_shader(const Shader &shader);
void set_compute_shader(const std::shared_ptr<Shader>& shader);
private:
uint width_;
uint height_;
RayTracerConfig config_;
Shader compute_shader_;
std::shared_ptr<Shader> compute_shader_;
TextureHandle accumulation_texture_;
BufferHandle scene_buffer_;
BufferHandle material_buffer_;

View File

@ -57,6 +57,9 @@ public:
/// @param config New configuration
void set_config(const RendererConfig& config);
/// @brief Notify scene changed to rebuild acceleration
void notify_scene_changed(const Scene &scene);
private:
RendererConfig config_;
std::unique_ptr<GBuffer> gbuffer_;

View File

@ -5,6 +5,7 @@
#include "resource/shader.h"
#include <unordered_map>
#include <string>
#include <memory>
namespace are {
@ -29,7 +30,7 @@ public:
/// @param vertex_path Vertex shader file path
/// @param fragment_path Fragment shader file path
/// @return Shader object
Shader load_shader(const std::string& name,
std::shared_ptr<Shader> load_shader(const std::string& name,
const std::string& vertex_path,
const std::string& fragment_path);
@ -37,26 +38,26 @@ public:
/// @param name Shader name for caching
/// @param compute_path Compute shader file path
/// @return Shader object
Shader load_compute_shader(const std::string& name,
std::shared_ptr<Shader> load_compute_shader(const std::string& name,
const std::string& compute_path);
/// @brief Get cached shader by name
/// @param name Shader name
/// @return Shader object (invalid if not found)
Shader get_shader(const std::string& name) const;
std::shared_ptr<Shader> get_shader(const std::string& name) const;
/// @brief Get G-Buffer shader
/// @return G-Buffer shader
const Shader& get_gbuffer_shader() const { return gbuffer_shader_; }
const std::shared_ptr<Shader>& get_gbuffer_shader() const { return gbuffer_shader_; }
/// @brief Get ray tracing compute shader
/// @return Ray tracing shader
const Shader& get_raytracing_shader() const { return raytracing_shader_; }
const std::shared_ptr<Shader>& get_raytracing_shader() const { return raytracing_shader_; }
private:
std::unordered_map<std::string, Shader> shader_cache_;
Shader gbuffer_shader_;
Shader raytracing_shader_;
std::unordered_map<std::string, std::shared_ptr<Shader>> shader_cache_;
std::shared_ptr<Shader> gbuffer_shader_;
std::shared_ptr<Shader> raytracing_shader_;
bool initialized_;

View File

@ -13,6 +13,12 @@ public:
/// @brief Constructor
Shader();
Shader(const Shader&) = delete;
Shader& operator=(const Shader&) = delete;
Shader(Shader&& other) noexcept;
Shader& operator=(Shader&& other) noexcept;
/// @brief Destructor
~Shader();

View File

@ -22,7 +22,7 @@ layout(binding = 0, rgba32f) uniform readonly image2D g_position;
layout(binding = 1, rgba32f) uniform readonly image2D g_normal;
layout(binding = 2, rgba8) uniform readonly image2D g_albedo;
// New: material params + material id
// Material params + material id (for primary hit fast-path)
layout(binding = 5, rgba32f) uniform readonly image2D g_material;
layout(binding = 6, r32ui) uniform readonly uimage2D g_material_id;
@ -96,7 +96,6 @@ uniform uint u_frame_count;
uniform uint u_samples_per_pixel;
uniform uint u_max_depth;
uniform uint u_light_count;
uniform vec3 u_camera_position;
uniform mat4 u_inv_view_projection;
uniform bool u_enable_accumulation;
uniform bool u_use_bvh;
@ -234,7 +233,6 @@ bool intersect_triangle(Ray ray, TriangleGpu tri, inout HitInfo hit) {
float t = dot(e2, qvec) * inv_det;
if (t < EPSILON || t >= hit.t) return false;
// Interpolate normal/uv
float w = 1.0 - u - v;
vec3 n0 = tri.n0.xyz;
vec3 n1 = tri.n1.xyz;
@ -265,7 +263,6 @@ HitInfo trace_ray_bvh(Ray ray) {
return hit;
}
// Small fixed stack
uint stack[64];
int sp = 0;
stack[sp++] = 0u;
@ -288,11 +285,8 @@ HitInfo trace_ray_bvh(Ray ray) {
intersect_triangle(ray, tri, hit);
}
} else {
// Interior: push children
uint left = left_first;
uint right = left_first + 1u;
// Depth-first; no sorting (simple)
if (sp < 63) stack[sp++] = right;
if (sp < 63) stack[sp++] = left;
}
@ -330,9 +324,7 @@ bool trace_any_bvh(Ray ray, float t_max) {
if (count > 0u) {
for (uint i = 0u; i < count; ++i) {
TriangleGpu tri = bvh_tris[left_first + i];
if (intersect_triangle(ray, tri, hit)) {
return true;
}
if (intersect_triangle(ray, tri, hit)) return true;
}
} else {
uint left = left_first;
@ -345,6 +337,45 @@ bool trace_any_bvh(Ray ray, float t_max) {
return false;
}
// ============================================================================
// Primary-ray fast path via G-Buffer
// ============================================================================
/**
* @brief Read primary hit from G-Buffer if current pixel has geometry
* @note Uses g_position.w as "valid" marker (your gbuffer writes 1.0 on hits, clear is 0).
*/
HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
HitInfo hit;
hit.hit = false;
hit.t = MAX_FLOAT;
hit.position = vec3(0.0);
hit.normal = vec3(0.0, 1.0, 0.0);
hit.texcoord = vec2(0.0);
hit.material_id = 0u;
vec4 pos = imageLoad(g_position, pixel_coords);
if (pos.w <= 0.5) {
return hit;
}
vec3 p = pos.xyz;
vec3 n = normalize(imageLoad(g_normal, pixel_coords).xyz);
// integer material id
uint mid = imageLoad(g_material_id, pixel_coords).r;
hit.hit = true;
hit.position = p;
hit.normal = n;
hit.material_id = mid;
// For RR/any debug usage; path tracing uses this as starting point only.
hit.t = length(p - ray.origin);
return hit;
}
// ============================================================================
// Material + scattering
// ============================================================================
@ -429,7 +460,6 @@ ScatterResult scatter_ray(Ray ray_in, HitInfo hit, Material mat, inout uint seed
vec3 eval_direct_lighting(HitInfo hit, Material mat, inout uint seed) {
if (u_light_count == 0u) return vec3(0.0);
// sample one light
uint light_idx = uint(random_float(seed) * float(u_light_count)) % u_light_count;
Light light = lights[light_idx];
@ -455,18 +485,15 @@ vec3 eval_direct_lighting(HitInfo hit, Material mat, inout uint seed) {
float n_dot_l = max(dot(hit.normal, L), 0.0);
if (n_dot_l <= 0.0) return vec3(0.0);
// shadow ray
Ray shadow_ray;
shadow_ray.origin = hit.position + hit.normal * EPSILON;
shadow_ray.direction = L;
float t_max = (light.type == LIGHT_POINT) ? (dist - EPSILON) : MAX_FLOAT;
if (trace_any_bvh(shadow_ray, t_max)) {
return vec3(0.0);
}
if (trace_any_bvh(shadow_ray, t_max)) return vec3(0.0);
float pdf_light = 1.0 / float(u_light_count);
vec3 brdf = mat.albedo * INV_PI; // diffuse direct only (simple)
vec3 brdf = mat.albedo * INV_PI;
return brdf * radiance * n_dot_l / max(pdf_light, EPSILON);
}
@ -489,15 +516,52 @@ Material fetch_material(uint material_id) {
}
vec3 environment_color(vec3 dir) {
// simple dark sky
return vec3(0.1, 0.1, 0.15);
}
vec3 trace_path(Ray ray, inout uint seed) {
Ray generate_camera_ray_center(ivec2 pixel_coords, ivec2 image_size) {
vec2 uv = (vec2(pixel_coords) + vec2(0.5)) / vec2(image_size);
vec2 ndc = uv * 2.0 - 1.0;
vec4 p_near = u_inv_view_projection * vec4(ndc, 0.0, 1.0);
vec4 p_far = u_inv_view_projection * vec4(ndc, 1.0, 1.0);
vec3 near_ws = p_near.xyz / p_near.w;
vec3 far_ws = p_far.xyz / p_far.w;
Ray r;
r.origin = near_ws;
r.direction = normalize(far_ws - near_ws);
return r;
}
/**
* @brief Trace path with primary-ray G-Buffer acceleration
*/
vec3 trace_path_primary_gbuffer(ivec2 pixel_coords, ivec2 image_size, inout uint seed) {
Ray ray = generate_camera_ray_center(pixel_coords, image_size);
vec3 radiance = vec3(0.0);
vec3 throughput = vec3(1.0);
for (uint depth = 0u; depth < u_max_depth; ++depth) {
// Depth 0: try G-Buffer hit first
HitInfo hit0 = trace_primary_gbuffer(ray, pixel_coords);
if (hit0.hit) {
Material mat0 = fetch_material(hit0.material_id);
radiance += throughput * mat0.emission;
if (mat0.type == MATERIAL_DIFFUSE) {
radiance += throughput * eval_direct_lighting(hit0, mat0, seed);
}
ScatterResult sc0 = scatter_ray(ray, hit0, mat0, seed);
if (!sc0.scattered) return radiance;
throughput *= sc0.attenuation;
ray = sc0.scattered_ray;
}
// Subsequent bounces: BVH
for (uint depth = (hit0.hit ? 1u : 0u); depth < u_max_depth; ++depth) {
HitInfo hit = trace_ray_bvh(ray);
if (!hit.hit) {
radiance += throughput * environment_color(ray.direction);
@ -506,10 +570,7 @@ vec3 trace_path(Ray ray, inout uint seed) {
Material mat = fetch_material(hit.material_id);
// emission
radiance += throughput * mat.emission;
// direct light (only for diffuse to keep simple)
if (mat.type == MATERIAL_DIFFUSE) {
radiance += throughput * eval_direct_lighting(hit, mat, seed);
}
@ -519,7 +580,6 @@ vec3 trace_path(Ray ray, inout uint seed) {
throughput *= sc.attenuation;
// RR
if (depth > 3u) {
float p = max(throughput.r, max(throughput.g, throughput.b));
p = clamp(p, 0.0, 0.95);
@ -544,12 +604,10 @@ void main() {
uint seed = base_seed + u_frame_count * 719393u;
vec3 color = vec3(0.0);
// Multi-sample
uint spp = max(u_samples_per_pixel, 1u);
for (uint s = 0u; s < spp; ++s) {
Ray cam_ray = generate_camera_ray(pixel_coords, image_size, seed);
color += trace_path(cam_ray, seed);
color += trace_path_primary_gbuffer(pixel_coords, image_size, seed);
}
color /= float(spp);

View File

@ -135,6 +135,11 @@ void BVH::build_recursive_(uint node_idx, uint first_prim, uint prim_count) {
int axis;
float split_pos;
float split_cost = find_best_split_(first_prim, prim_count, axis, split_pos);
if(split_cost == std::numeric_limits<float>::max()) {
node.left_first_ = first_prim;
node.count_ = prim_count;
return;
}
// Check if split is beneficial
float no_split_cost = prim_count * bounds.surface_area();
@ -180,6 +185,7 @@ void BVH::build_recursive_(uint node_idx, uint first_prim, uint prim_count) {
float BVH::find_best_split_(uint first_prim, uint prim_count, int& axis, float& split_pos) {
float best_cost = std::numeric_limits<float>::max();
axis = 0, split_pos = 0.0f;
AABB centroid_bounds = calculate_centroid_bounds_(first_prim, prim_count);

View File

@ -1,319 +1,315 @@
#include "core/raytracer.h"
#include "utils/logger.h"
#include "basic/constants.h"
#include "utils/logger.h"
#include <glad/glad.h>
namespace are {
RayTracer::RayTracer(uint width, uint height, const RayTracerConfig& config)
: width_(width)
, height_(height)
, config_(config)
, accumulation_texture_(INVALID_HANDLE)
, scene_buffer_(INVALID_HANDLE)
, material_buffer_(INVALID_HANDLE)
, light_buffer_(INVALID_HANDLE)
, bvh_(nullptr)
, bvh_built_(false)
, frame_count_(0)
, initialized_(false) {
RayTracer::RayTracer(uint width, uint height, const RayTracerConfig &config)
: width_(width)
, height_(height)
, config_(config)
, accumulation_texture_(INVALID_HANDLE)
, scene_buffer_(INVALID_HANDLE)
, material_buffer_(INVALID_HANDLE)
, light_buffer_(INVALID_HANDLE)
, bvh_(nullptr)
, bvh_built_(false)
, frame_count_(0)
, initialized_(false) {
}
RayTracer::~RayTracer() {
release();
release();
}
bool RayTracer::initialize() {
if (initialized_) {
Logger::warning("RayTracer already initialized");
return true;
}
if (initialized_) {
Logger::warning("RayTracer already initialized");
return true;
}
// Create accumulation texture
glGenTextures(1, &accumulation_texture_);
glBindTexture(GL_TEXTURE_2D, accumulation_texture_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width_, height_, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Create accumulation texture
glGenTextures(1, &accumulation_texture_);
glBindTexture(GL_TEXTURE_2D, accumulation_texture_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width_, height_, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Create shader storage buffers
glGenBuffers(1, &material_buffer_);
glGenBuffers(1, &light_buffer_);
// Create shader storage buffers
glGenBuffers(1, &material_buffer_);
glGenBuffers(1, &light_buffer_);
// Load compute shader
Logger::info("Loading ray tracing compute shader in RayTracer...");
if (!compute_shader_.load_compute("shaders/raytracing.comp")) {
Logger::error("Failed to load ray tracing compute shader in RayTracer");
return false;
}
Logger::info("Ray tracing compute shader loaded in RayTracer");
// Initialize BVH if enabled
if (config_.use_bvh_) {
bvh_ = std::make_unique<BVH>();
}
// Initialize BVH if enabled
if (config_.use_bvh_) {
bvh_ = std::make_unique<BVH>();
}
initialized_ = true;
Logger::info("RayTracer initialized successfully");
return true;
initialized_ = true;
Logger::info("RayTracer initialized successfully");
return true;
}
void RayTracer::release() {
if (!initialized_) return;
if (!initialized_)
return;
if (accumulation_texture_ != INVALID_HANDLE) {
glDeleteTextures(1, &accumulation_texture_);
accumulation_texture_ = INVALID_HANDLE;
}
if (accumulation_texture_ != INVALID_HANDLE) {
glDeleteTextures(1, &accumulation_texture_);
accumulation_texture_ = INVALID_HANDLE;
}
if (material_buffer_ != INVALID_HANDLE) {
glDeleteBuffers(1, &material_buffer_);
material_buffer_ = INVALID_HANDLE;
}
if (material_buffer_ != INVALID_HANDLE) {
glDeleteBuffers(1, &material_buffer_);
material_buffer_ = INVALID_HANDLE;
}
if (light_buffer_ != INVALID_HANDLE) {
glDeleteBuffers(1, &light_buffer_);
light_buffer_ = INVALID_HANDLE;
}
if (light_buffer_ != INVALID_HANDLE) {
glDeleteBuffers(1, &light_buffer_);
light_buffer_ = INVALID_HANDLE;
}
bvh_node_buffer_.release();
bvh_triangle_buffer_.release();
bvh_node_buffer_.release();
bvh_triangle_buffer_.release();
compute_shader_.release();
bvh_.reset();
bvh_built_ = false;
bvh_.reset();
bvh_built_ = false;
initialized_ = false;
Logger::info("RayTracer released");
initialized_ = false;
Logger::info("RayTracer released");
}
bool RayTracer::rebuild_bvh(const Scene& scene) {
if (!config_.use_bvh_) {
Logger::warning("BVH is disabled in configuration");
return false;
}
bool RayTracer::rebuild_bvh(const Scene &scene) {
if (!config_.use_bvh_) {
Logger::warning("BVH is disabled in configuration");
return false;
}
if (!bvh_) {
bvh_ = std::make_unique<BVH>();
}
if (!bvh_) {
bvh_ = std::make_unique<BVH>();
}
Logger::info("Building BVH for ray tracing...");
Logger::info("Building BVH for ray tracing...");
if (!bvh_->build(scene.get_meshes())) {
Logger::error("Failed to build BVH");
return false;
}
if (!bvh_->build(scene.get_meshes())) {
Logger::error("Failed to build BVH");
return false;
}
if (!bvh_->upload_to_gpu(bvh_node_buffer_, bvh_triangle_buffer_)) {
Logger::error("Failed to upload BVH to GPU");
return false;
}
if (!bvh_->upload_to_gpu(bvh_node_buffer_, bvh_triangle_buffer_)) {
Logger::error("Failed to upload BVH to GPU");
return false;
}
bvh_built_ = true;
Logger::info("BVH built and uploaded successfully");
return true;
bvh_built_ = true;
Logger::info("BVH built and uploaded successfully");
return true;
}
void RayTracer::trace(const Scene& scene, const GBuffer& gbuffer, TextureHandle output_texture) {
if (!initialized_) {
Logger::error("RayTracer not initialized");
return;
}
void RayTracer::trace(const Scene &scene, const GBuffer &gbuffer, TextureHandle output_texture) {
if (!initialized_) {
Logger::error("RayTracer not initialized");
return;
}
if (!compute_shader_.is_valid()) {
Logger::error("Ray tracing compute shader not loaded");
return;
}
if (!compute_shader_->is_valid()) {
Logger::error("Ray tracing compute shader not loaded");
return;
}
// Build BVH if enabled and not built yet
if (config_.use_bvh_ && !bvh_built_) {
rebuild_bvh(scene);
}
// Build BVH if enabled and not built yet
if (config_.use_bvh_ && !bvh_built_) {
rebuild_bvh(scene);
}
// Upload scene data
upload_scene_data_(scene);
// Upload scene data
upload_scene_data_(scene);
// Use compute shader
compute_shader_.use();
// Use compute shader
if (!compute_shader_ || !compute_shader_->is_valid()) {
Logger::error("Ray tracing compute shader not set or invalid");
return;
}
compute_shader_->use();
// Bind G-Buffer textures
bind_gbuffer_(gbuffer);
// Bind G-Buffer textures
bind_gbuffer_(gbuffer);
// Bind output and accumulation textures
glBindImageTexture(3, output_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
glBindImageTexture(4, accumulation_texture_, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);
// Bind output and accumulation textures
glBindImageTexture(3, output_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
glBindImageTexture(4, accumulation_texture_, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);
// Bind BVH buffers if enabled
if (config_.use_bvh_ && bvh_built_) {
bvh_node_buffer_.bind_base(2);
bvh_triangle_buffer_.bind_base(3);
compute_shader_.set_bool("u_use_bvh", true);
compute_shader_.set_uint("u_bvh_node_count", bvh_->get_node_count());
} else {
compute_shader_.set_bool("u_use_bvh", false);
}
// Bind BVH buffers if enabled
if (config_.use_bvh_ && bvh_built_) {
bvh_node_buffer_.bind_base(2);
bvh_triangle_buffer_.bind_base(3);
compute_shader_->set_bool("u_use_bvh", true);
compute_shader_->set_uint("u_bvh_node_count", bvh_->get_node_count());
} else {
compute_shader_->set_bool("u_use_bvh", false);
}
// Set uniforms
compute_shader_.set_uint("u_frame_count", frame_count_);
compute_shader_.set_uint("u_samples_per_pixel", config_.samples_per_pixel_);
compute_shader_.set_uint("u_max_depth", config_.max_depth_);
compute_shader_.set_uint("u_light_count", static_cast<uint>(scene.get_lights().size()));
compute_shader_.set_bool("u_enable_accumulation", config_.enable_accumulation_);
// Set uniforms
compute_shader_->set_uint("u_frame_count", frame_count_);
compute_shader_->set_uint("u_samples_per_pixel", config_.samples_per_pixel_);
compute_shader_->set_uint("u_max_depth", config_.max_depth_);
compute_shader_->set_uint("u_light_count", static_cast<uint>(scene.get_lights().size()));
compute_shader_->set_bool("u_enable_accumulation", config_.enable_accumulation_);
// Set camera data
const Camera& camera = scene.get_camera();
compute_shader_.set_vec3("u_camera_position", camera.get_position());
// Set camera data
const Camera &camera = scene.get_camera();
compute_shader_->set_vec3("u_camera_position", camera.get_position());
Mat4 inv_vp = glm::inverse(camera.get_view_projection_matrix());
compute_shader_.set_mat4("u_inv_view_projection", inv_vp);
Mat4 inv_vp = glm::inverse(camera.get_view_projection_matrix());
compute_shader_->set_mat4("u_inv_view_projection", inv_vp);
// Dispatch compute shader
uint num_groups_x = (width_ + COMPUTE_GROUP_SIZE_X - 1) / COMPUTE_GROUP_SIZE_X;
uint num_groups_y = (height_ + COMPUTE_GROUP_SIZE_Y - 1) / COMPUTE_GROUP_SIZE_Y;
// Dispatch compute shader
uint num_groups_x = (width_ + COMPUTE_GROUP_SIZE_X - 1) / COMPUTE_GROUP_SIZE_X;
uint num_groups_y = (height_ + COMPUTE_GROUP_SIZE_Y - 1) / COMPUTE_GROUP_SIZE_Y;
glDispatchCompute(num_groups_x, num_groups_y, 1);
glDispatchCompute(num_groups_x, num_groups_y, 1);
// Memory barrier
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
// Memory barrier
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
// Increment frame count for accumulation
if (config_.enable_accumulation_) {
frame_count_++;
}
// Increment frame count for accumulation
if (config_.enable_accumulation_) {
frame_count_++;
}
}
void RayTracer::resize(uint width, uint height) {
if (width == width_ && height == height_) return;
if (width == width_ && height == height_)
return;
width_ = width;
height_ = height;
width_ = width;
height_ = height;
if (initialized_) {
// Recreate accumulation texture
if (accumulation_texture_ != INVALID_HANDLE) {
glDeleteTextures(1, &accumulation_texture_);
}
if (initialized_) {
// Recreate accumulation texture
if (accumulation_texture_ != INVALID_HANDLE) {
glDeleteTextures(1, &accumulation_texture_);
}
glGenTextures(1, &accumulation_texture_);
glBindTexture(GL_TEXTURE_2D, accumulation_texture_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width_, height_, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glGenTextures(1, &accumulation_texture_);
glBindTexture(GL_TEXTURE_2D, accumulation_texture_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width_, height_, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
reset_accumulation();
}
reset_accumulation();
}
}
void RayTracer::reset_accumulation() {
frame_count_ = 0;
frame_count_ = 0;
}
void RayTracer::set_config(const RayTracerConfig& config) {
bool bvh_changed = (config.use_bvh_ != config_.use_bvh_);
void RayTracer::set_config(const RayTracerConfig &config) {
bool bvh_changed = (config.use_bvh_ != config_.use_bvh_);
config_ = config;
reset_accumulation();
config_ = config;
reset_accumulation();
if (bvh_changed) {
if (config_.use_bvh_ && !bvh_) {
bvh_ = std::make_unique<BVH>();
bvh_built_ = false;
} else if (!config_.use_bvh_) {
bvh_.reset();
bvh_built_ = false;
}
}
if (bvh_changed) {
if (config_.use_bvh_ && !bvh_) {
bvh_ = std::make_unique<BVH>();
bvh_built_ = false;
} else if (!config_.use_bvh_) {
bvh_.reset();
bvh_built_ = false;
}
}
}
void RayTracer::upload_scene_data_(const Scene& scene) {
// Upload materials
const auto& materials = scene.get_materials();
if (!materials.empty()) {
struct MaterialData {
Vec3 albedo;
float metallic;
Vec3 emission;
float roughness;
int type;
float ior;
Vec2 padding;
};
void RayTracer::upload_scene_data_(const Scene &scene) {
// Upload materials
const auto &materials = scene.get_materials();
if (!materials.empty()) {
struct MaterialData {
Vec3 albedo;
float metallic;
Vec3 emission;
float roughness;
int type;
float ior;
Vec2 padding;
};
std::vector<MaterialData> material_data;
material_data.reserve(materials.size());
std::vector<MaterialData> material_data;
material_data.reserve(materials.size());
for (const auto& mat : materials) {
MaterialData data;
data.albedo = mat->get_albedo();
data.metallic = mat->get_metallic();
data.emission = mat->get_emission();
data.roughness = mat->get_roughness();
data.type = static_cast<int>(mat->get_type());
data.ior = mat->get_ior();
material_data.push_back(data);
}
for (const auto &mat : materials) {
MaterialData data;
data.albedo = mat->get_albedo();
data.metallic = mat->get_metallic();
data.emission = mat->get_emission();
data.roughness = mat->get_roughness();
data.type = static_cast<int>(mat->get_type());
data.ior = mat->get_ior();
material_data.push_back(data);
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, material_buffer_);
glBufferData(GL_SHADER_STORAGE_BUFFER,
material_data.size() * sizeof(MaterialData),
material_data.data(), GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, material_buffer_);
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, material_buffer_);
glBufferData(GL_SHADER_STORAGE_BUFFER,
material_data.size() * sizeof(MaterialData),
material_data.data(), GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, material_buffer_);
}
// Upload lights
const auto& lights = scene.get_lights();
if (!lights.empty()) {
struct LightData {
Vec3 position;
int type;
Vec3 direction;
float intensity;
Vec3 color;
float range;
Vec2 spot_angles;
Vec2 padding;
};
// Upload lights
const auto &lights = scene.get_lights();
if (!lights.empty()) {
struct LightData {
Vec3 position;
int type;
Vec3 direction;
float intensity;
Vec3 color;
float range;
Vec2 spot_angles;
Vec2 padding;
};
std::vector<LightData> light_data;
light_data.reserve(lights.size());
std::vector<LightData> light_data;
light_data.reserve(lights.size());
for (const auto& light : lights) {
LightData data;
data.position = light->get_position();
data.type = static_cast<int>(light->get_type());
data.direction = light->get_direction();
data.intensity = light->get_intensity();
data.color = light->get_color();
data.range = light->get_range();
data.spot_angles = Vec2(light->get_inner_angle(), light->get_outer_angle());
light_data.push_back(data);
}
for (const auto &light : lights) {
LightData data;
data.position = light->get_position();
data.type = static_cast<int>(light->get_type());
data.direction = light->get_direction();
data.intensity = light->get_intensity();
data.color = light->get_color();
data.range = light->get_range();
data.spot_angles = Vec2(light->get_inner_angle(), light->get_outer_angle());
light_data.push_back(data);
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, light_buffer_);
glBufferData(GL_SHADER_STORAGE_BUFFER,
light_data.size() * sizeof(LightData),
light_data.data(), GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, light_buffer_);
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, light_buffer_);
glBufferData(GL_SHADER_STORAGE_BUFFER,
light_data.size() * sizeof(LightData),
light_data.data(), GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, light_buffer_);
}
}
void RayTracer::bind_gbuffer_(const GBuffer& gbuffer) {
glBindImageTexture(0, gbuffer.get_texture(GBUFFER_POSITION), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
glBindImageTexture(1, gbuffer.get_texture(GBUFFER_NORMAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
glBindImageTexture(2, gbuffer.get_texture(GBUFFER_ALBEDO), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
void RayTracer::bind_gbuffer_(const GBuffer &gbuffer) {
glBindImageTexture(0, gbuffer.get_texture(GBUFFER_POSITION), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
glBindImageTexture(1, gbuffer.get_texture(GBUFFER_NORMAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
glBindImageTexture(2, gbuffer.get_texture(GBUFFER_ALBEDO), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
glBindImageTexture(5, gbuffer.get_texture(GBUFFER_MATERIAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
glBindImageTexture(6, gbuffer.get_texture(GBUFFER_MATERIAL_ID), 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);
glBindImageTexture(5, gbuffer.get_texture(GBUFFER_MATERIAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
glBindImageTexture(6, gbuffer.get_texture(GBUFFER_MATERIAL_ID), 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);
}
void RayTracer::set_compute_shader(const Shader& shader) {
compute_shader_ = shader;
Logger::info("Compute shader set for RayTracer");
void RayTracer::set_compute_shader(const std::shared_ptr<Shader> &shader) {
compute_shader_ = shader;
Logger::info("Compute shader set for RayTracer");
}
} // namespace are

View File

@ -53,12 +53,12 @@ bool Renderer::initialize() {
}
// Pass compute shader to ray tracer
const Shader& rt_shader = shader_manager_->get_raytracing_shader();
if (!rt_shader.is_valid()) {
Logger::error("Ray tracing shader is invalid");
return false;
}
raytracer_->set_compute_shader(rt_shader);
const auto& rt_shader = shader_manager_->get_raytracing_shader();
if (!rt_shader || !rt_shader->is_valid()) {
Logger::error("Ray tracing shader is invalid");
return false;
}
raytracer_->set_compute_shader(rt_shader);
// Initialize screen blit
screen_blit_ = std::make_unique<ScreenBlit>();
@ -116,8 +116,12 @@ RenderStats Renderer::render(const Scene& scene, TextureHandle output_texture) {
// Phase 1: G-Buffer pass
auto gbuffer_start = std::chrono::high_resolution_clock::now();
const Shader& gbuffer_shader = shader_manager_->get_gbuffer_shader();
gbuffer_->render(scene, gbuffer_shader);
const auto& gbuffer_shader = shader_manager_->get_gbuffer_shader();
if (!gbuffer_shader || !gbuffer_shader->is_valid()) {
Logger::error("G-Buffer shader is invalid");
return stats;
}
gbuffer_->render(scene, *gbuffer_shader);
auto gbuffer_end = std::chrono::high_resolution_clock::now();
stats.gbuffer_time_ms_ = std::chrono::duration<float, std::milli>(gbuffer_end - gbuffer_start).count();
@ -202,4 +206,9 @@ void Renderer::set_config(const RendererConfig &config) {
}
}
void Renderer::notify_scene_changed(const Scene &scene) {
raytracer_->reset_accumulation();
raytracer_->rebuild_bvh(scene);
}
} // namespace are

View File

@ -32,33 +32,31 @@ bool ShaderManager::initialize() {
void ShaderManager::release() {
if (!initialized_) return;
gbuffer_shader_.release();
raytracing_shader_.release();
for (auto& pair : shader_cache_) {
pair.second.release();
if (pair.second) pair.second->release();
}
shader_cache_.clear();
gbuffer_shader_.reset();
raytracing_shader_.reset();
initialized_ = false;
Logger::info("ShaderManager released");
}
Shader ShaderManager::load_shader(const std::string& name,
const std::string& vertex_path,
const std::string& fragment_path) {
// Check cache
std::shared_ptr<Shader> ShaderManager::load_shader(const std::string& name,
const std::string& vertex_path,
const std::string& fragment_path) {
auto it = shader_cache_.find(name);
if (it != shader_cache_.end()) {
Logger::info("Shader '" + name + "' loaded from cache");
return it->second;
}
// Load shader
Shader shader;
if (!shader.load(vertex_path, fragment_path)) {
auto shader = std::make_shared<Shader>();
if (!shader->load(vertex_path, fragment_path)) {
Logger::error("Failed to load shader '" + name + "'");
return Shader();
return nullptr;
}
shader_cache_[name] = shader;
@ -66,20 +64,18 @@ Shader ShaderManager::load_shader(const std::string& name,
return shader;
}
Shader ShaderManager::load_compute_shader(const std::string& name,
const std::string& compute_path) {
// Check cache
std::shared_ptr<Shader> ShaderManager::load_compute_shader(const std::string& name,
const std::string& compute_path) {
auto it = shader_cache_.find(name);
if (it != shader_cache_.end()) {
Logger::info("Compute shader '" + name + "' loaded from cache");
return it->second;
}
// Load shader
Shader shader;
if (!shader.load_compute(compute_path)) {
auto shader = std::make_shared<Shader>();
if (!shader->load_compute(compute_path)) {
Logger::error("Failed to load compute shader '" + name + "'");
return Shader();
return nullptr;
}
shader_cache_[name] = shader;
@ -87,27 +83,25 @@ Shader ShaderManager::load_compute_shader(const std::string& name,
return shader;
}
Shader ShaderManager::get_shader(const std::string& name) const {
std::shared_ptr<Shader> ShaderManager::get_shader(const std::string& name) const {
auto it = shader_cache_.find(name);
if (it != shader_cache_.end()) {
return it->second;
}
if (it != shader_cache_.end()) return it->second;
Logger::warning("Shader '" + name + "' not found in cache");
return Shader();
return nullptr;
}
bool ShaderManager::load_builtin_shaders_() {
// Load G-Buffer shader
if (!gbuffer_shader_.load("shaders/gbuffer.vert", "shaders/gbuffer.frag")) {
gbuffer_shader_ = std::make_shared<Shader>();
if (!gbuffer_shader_->load("shaders/gbuffer.vert", "shaders/gbuffer.frag")) {
Logger::error("Failed to load G-Buffer shader");
return false;
}
shader_cache_["gbuffer"] = gbuffer_shader_;
// Load ray tracing compute shader
Logger::info("Loading ray tracing compute shader...");
if (!raytracing_shader_.load_compute("shaders/raytracing.comp")) {
raytracing_shader_ = std::make_shared<Shader>();
if (!raytracing_shader_->load_compute("shaders/raytracing.comp")) {
Logger::error("Failed to load ray tracing shader");
return false;
}

View File

@ -11,10 +11,29 @@ Shader::Shader()
: handle_(INVALID_HANDLE) {
}
Shader::Shader(Shader&& other) noexcept
: handle_(other.handle_)
, uniform_cache_(std::move(other.uniform_cache_)) {
other.handle_ = INVALID_HANDLE;
other.uniform_cache_.clear();
}
Shader::~Shader() {
// Don't auto-release, let user control lifetime
}
Shader& Shader::operator=(Shader&& other) noexcept {
if (this == &other) return *this;
release();
handle_ = other.handle_;
uniform_cache_ = std::move(other.uniform_cache_);
other.handle_ = INVALID_HANDLE;
other.uniform_cache_.clear();
return *this;
}
bool Shader::load(const std::string& vertex_path, const std::string& fragment_path) {
std::string vertex_source = read_file_(vertex_path);
std::string fragment_source = read_file_(fragment_path);