diff --git a/examples/cornell_box b/examples/cornell_box index befa73f..5ccfb29 100644 Binary files a/examples/cornell_box and b/examples/cornell_box differ diff --git a/examples/cornell_box.cpp b/examples/cornell_box.cpp index 3b20586..fec2dda 100644 --- a/examples/cornell_box.cpp +++ b/examples/cornell_box.cpp @@ -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; diff --git a/include/core/raytracer.h b/include/core/raytracer.h index abe4d1d..fa8a3b2 100644 --- a/include/core/raytracer.h +++ b/include/core/raytracer.h @@ -7,6 +7,7 @@ #include "resource/buffer.h" #include "resource/shader.h" #include "scene/scene.h" +#include 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); private: uint width_; uint height_; RayTracerConfig config_; - Shader compute_shader_; + std::shared_ptr compute_shader_; TextureHandle accumulation_texture_; BufferHandle scene_buffer_; BufferHandle material_buffer_; diff --git a/include/core/renderer.h b/include/core/renderer.h index 9040710..b0b29b6 100644 --- a/include/core/renderer.h +++ b/include/core/renderer.h @@ -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_; diff --git a/include/core/shader_manager.h b/include/core/shader_manager.h index 891dccc..c48665a 100644 --- a/include/core/shader_manager.h +++ b/include/core/shader_manager.h @@ -5,6 +5,7 @@ #include "resource/shader.h" #include #include +#include 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 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 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 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& 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& get_raytracing_shader() const { return raytracing_shader_; } private: - std::unordered_map shader_cache_; - Shader gbuffer_shader_; - Shader raytracing_shader_; + std::unordered_map> shader_cache_; + std::shared_ptr gbuffer_shader_; + std::shared_ptr raytracing_shader_; bool initialized_; diff --git a/include/resource/shader.h b/include/resource/shader.h index 89e8175..25ea338 100644 --- a/include/resource/shader.h +++ b/include/resource/shader.h @@ -12,6 +12,12 @@ class Shader { 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(); diff --git a/shaders/raytracing.comp b/shaders/raytracing.comp index 52b9d93..a34c5eb 100644 --- a/shaders/raytracing.comp +++ b/shaders/raytracing.comp @@ -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); diff --git a/src/core/bvh.cpp b/src/core/bvh.cpp index e1c747e..7639f9a 100644 --- a/src/core/bvh.cpp +++ b/src/core/bvh.cpp @@ -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::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::max(); + axis = 0, split_pos = 0.0f; AABB centroid_bounds = calculate_centroid_bounds_(first_prim, prim_count); diff --git a/src/core/raytracer.cpp b/src/core/raytracer.cpp index 4525357..e110708 100644 --- a/src/core/raytracer.cpp +++ b/src/core/raytracer.cpp @@ -1,319 +1,315 @@ #include "core/raytracer.h" -#include "utils/logger.h" #include "basic/constants.h" +#include "utils/logger.h" #include 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; - } - - // 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_); - - // 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(); - } - - initialized_ = true; - Logger::info("RayTracer initialized successfully"); - 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 shader storage buffers + glGenBuffers(1, &material_buffer_); + glGenBuffers(1, &light_buffer_); + + // Initialize BVH if enabled + if (config_.use_bvh_) { + bvh_ = std::make_unique(); + } + + initialized_ = true; + Logger::info("RayTracer initialized successfully"); + return true; } void RayTracer::release() { - if (!initialized_) return; - - 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 (light_buffer_ != INVALID_HANDLE) { - glDeleteBuffers(1, &light_buffer_); - light_buffer_ = INVALID_HANDLE; - } - - bvh_node_buffer_.release(); - bvh_triangle_buffer_.release(); - - compute_shader_.release(); - - bvh_.reset(); - bvh_built_ = false; - - initialized_ = false; - Logger::info("RayTracer released"); + if (!initialized_) + return; + + 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 (light_buffer_ != INVALID_HANDLE) { + glDeleteBuffers(1, &light_buffer_); + light_buffer_ = INVALID_HANDLE; + } + + bvh_node_buffer_.release(); + bvh_triangle_buffer_.release(); + + bvh_.reset(); + bvh_built_ = false; + + 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; - } - - if (!bvh_) { - bvh_ = std::make_unique(); - } - - Logger::info("Building BVH for ray tracing..."); - - 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; - } - - bvh_built_ = true; - Logger::info("BVH built and uploaded successfully"); - return true; +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(); + } + + Logger::info("Building BVH for ray tracing..."); + + 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; + } + + 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; - } - - 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); - } - - // Upload scene data - upload_scene_data_(scene); - - // Use compute shader - compute_shader_.use(); - - // 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 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(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()); - - 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; - - glDispatchCompute(num_groups_x, num_groups_y, 1); - - // Memory barrier - glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); - - // Increment frame count for accumulation - if (config_.enable_accumulation_) { - frame_count_++; - } +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; + } + + // Build BVH if enabled and not built yet + if (config_.use_bvh_ && !bvh_built_) { + rebuild_bvh(scene); + } + + // Upload scene data + upload_scene_data_(scene); + + // 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 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); + } + + // 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(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()); + + 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; + + glDispatchCompute(num_groups_x, num_groups_y, 1); + + // Memory barrier + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + // Increment frame count for accumulation + if (config_.enable_accumulation_) { + frame_count_++; + } } void RayTracer::resize(uint width, uint height) { - if (width == width_ && height == height_) return; - - width_ = width; - height_ = height; - - 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); - - reset_accumulation(); - } + if (width == width_ && height == height_) + return; + + width_ = width; + height_ = height; + + 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); + + 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_); - - config_ = config; - reset_accumulation(); - - if (bvh_changed) { - if (config_.use_bvh_ && !bvh_) { - bvh_ = std::make_unique(); - bvh_built_ = false; - } else if (!config_.use_bvh_) { - bvh_.reset(); - bvh_built_ = false; - } - } +void RayTracer::set_config(const RayTracerConfig &config) { + bool bvh_changed = (config.use_bvh_ != config_.use_bvh_); + + config_ = config; + reset_accumulation(); + + if (bvh_changed) { + if (config_.use_bvh_ && !bvh_) { + bvh_ = std::make_unique(); + 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; - }; - - std::vector 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(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_); - } - - // 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 light_data; - light_data.reserve(lights.size()); - - for (const auto& light : lights) { - LightData data; - data.position = light->get_position(); - data.type = static_cast(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_); - } +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 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(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_); + } + + // 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 light_data; + light_data.reserve(lights.size()); + + for (const auto &light : lights) { + LightData data; + data.position = light->get_position(); + data.type = static_cast(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_); + } } -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) { + compute_shader_ = shader; + Logger::info("Compute shader set for RayTracer"); } } // namespace are diff --git a/src/core/renderer.cpp b/src/core/renderer.cpp index 056c6d3..bb1d2ec 100644 --- a/src/core/renderer.cpp +++ b/src/core/renderer.cpp @@ -53,13 +53,13 @@ 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(); if (!screen_blit_->initialize()) { @@ -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(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 diff --git a/src/core/shader_manager.cpp b/src/core/shader_manager.cpp index 88cd629..6c9c48f 100644 --- a/src/core/shader_manager.cpp +++ b/src/core/shader_manager.cpp @@ -31,83 +31,77 @@ 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 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(); + if (!shader->load(vertex_path, fragment_path)) { Logger::error("Failed to load shader '" + name + "'"); - return Shader(); + return nullptr; } - + shader_cache_[name] = shader; Logger::info("Shader '" + name + "' loaded successfully"); return shader; } -Shader ShaderManager::load_compute_shader(const std::string& name, - const std::string& compute_path) { - // Check cache +std::shared_ptr 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(); + if (!shader->load_compute(compute_path)) { Logger::error("Failed to load compute shader '" + name + "'"); - return Shader(); + return nullptr; } - + shader_cache_[name] = shader; Logger::info("Compute shader '" + name + "' loaded successfully"); return shader; } -Shader ShaderManager::get_shader(const std::string& name) const { +std::shared_ptr 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(); + 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(); + if (!raytracing_shader_->load_compute("shaders/raytracing.comp")) { Logger::error("Failed to load ray tracing shader"); return false; } diff --git a/src/resource/shader.cpp b/src/resource/shader.cpp index 73aa0f5..ae7e62a 100644 --- a/src/resource/shader.cpp +++ b/src/resource/shader.cpp @@ -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);