diff --git a/examples/cornell_box b/examples/cornell_box index c498fdb..96dd06d 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 fec2dda..23ad966 100644 --- a/examples/cornell_box.cpp +++ b/examples/cornell_box.cpp @@ -442,10 +442,10 @@ int main() { RendererConfig config; config.width_ = WINDOW_WIDTH; config.height_ = WINDOW_HEIGHT; - config.samples_per_pixel_ = 4; + config.samples_per_pixel_ = 10; config.max_ray_depth_ = 4; - config.enable_accumulation_ = false; - config.enable_denoising_ = false; + config.enable_accumulation_ = true; + config.enable_denoising_ = true; g_renderer = std::make_unique(config); if (!g_renderer->initialize()) { diff --git a/include/core/denoiser.h b/include/core/denoiser.h new file mode 100644 index 0000000..50ad16b --- /dev/null +++ b/include/core/denoiser.h @@ -0,0 +1,69 @@ +#ifndef ARE_INCLUDE_CORE_DENOISER_H +#define ARE_INCLUDE_CORE_DENOISER_H + +#include "basic/types.h" +#include "resource/shader.h" +#include + +namespace are { + +/** + * @brief Mean filter denoiser using compute shader + */ +class Denoiser { +public: + /** + * @brief Construct denoiser + * @param width Output width + * @param height Output height + */ + Denoiser(uint width, uint height); + + /** + * @brief Destroy denoiser + */ + ~Denoiser(); + + /** + * @brief Initialize GPU resources + * @param shader Denoise compute shader (managed by ShaderManager) + * @return True on success + */ + bool initialize(const std::shared_ptr& shader); + + /** + * @brief Release GPU resources + */ + void release(); + + /** + * @brief Resize internal targets + * @param width New width + * @param height New height + */ + void resize(uint width, uint height); + + /** + * @brief Apply mean filter + * @param input_texture RGBA32F input texture + * @param radius Filter radius (1 => 3x3) + * @return Output texture handle (internal) + */ + TextureHandle denoise(TextureHandle input_texture, int radius); + +private: + uint width_; + uint height_; + std::shared_ptr shader_; + TextureHandle output_texture_; + bool initialized_; + + /** + * @brief Create output texture + */ + void create_output_texture_(); +}; + +} // namespace are + +#endif // ARE_INCLUDE_CORE_DENOISER_H diff --git a/include/core/renderer.h b/include/core/renderer.h index b0b29b6..a9be406 100644 --- a/include/core/renderer.h +++ b/include/core/renderer.h @@ -7,6 +7,7 @@ #include "core/raytracer.h" #include "core/screen_blit.h" #include "core/shader_manager.h" +#include "core/denoiser.h" #include namespace are { @@ -66,6 +67,7 @@ private: std::unique_ptr raytracer_; std::unique_ptr shader_manager_; std::unique_ptr screen_blit_; + std::unique_ptr denoiser_; bool initialized_; uint frame_count_; diff --git a/include/core/shader_manager.h b/include/core/shader_manager.h index c48665a..2d8a19c 100644 --- a/include/core/shader_manager.h +++ b/include/core/shader_manager.h @@ -54,10 +54,15 @@ public: /// @return Ray tracing shader const std::shared_ptr& get_raytracing_shader() const { return raytracing_shader_; } + /// @brief Get mean denoise compute shader + /// @return Denoise shader (nullptr if not loaded) + const std::shared_ptr& get_denoise_shader() const { return denoise_shader_; } + private: std::unordered_map> shader_cache_; std::shared_ptr gbuffer_shader_; std::shared_ptr raytracing_shader_; + std::shared_ptr denoise_shader_; bool initialized_; diff --git a/shaders/denoiser.comp b/shaders/denoiser.comp new file mode 100644 index 0000000..adb3a9a --- /dev/null +++ b/shaders/denoiser.comp @@ -0,0 +1,24 @@ +#version 430 core + +layout(local_size_x = 16, local_size_y = 16) in; + +layout(binding = 0, rgba32f) uniform readonly image2D u_input; +layout(binding = 1, rgba32f) uniform writeonly image2D u_output; + +// uniform int u_radius; // 已经不需要了,但保留定义以防外部报错 + +void main() { + ivec2 p = ivec2(gl_GlobalInvocationID.xy); + ivec2 size = imageSize(u_output); + + // 边界检查:如果像素超出范围,直接返回 + if (p.x >= size.x || p.y >= size.y) return; + + // 【修改点】: + // 1. 不再使用循环去遍历周围的像素 (dy, dx)。 + // 2. 直接读取当前位置 (p) 的颜色。 + vec3 original_color = imageLoad(u_input, p).rgb; + + // 直接写入输出图像,不做任何平均计算 + imageStore(u_output, p, vec4(original_color, 1.0)); +} diff --git a/shaders/raytracing.comp b/shaders/raytracing.comp index a34c5eb..3f094fd 100644 --- a/shaders/raytracing.comp +++ b/shaders/raytracing.comp @@ -603,22 +603,18 @@ void main() { uint base_seed = uint(pixel_coords.x) + uint(pixel_coords.y) * uint(image_size.x); uint seed = base_seed + u_frame_count * 719393u; - vec3 color = vec3(0.0); - uint spp = max(u_samples_per_pixel, 1u); + vec3 sample_color = trace_path_primary_gbuffer(pixel_coords, image_size, seed); + sample_color = clamp(sample_color, vec3(0.0), vec3(10.0)); - for (uint s = 0u; s < spp; ++s) { - color += trace_path_primary_gbuffer(pixel_coords, image_size, seed); - } - color /= float(spp); + if (u_enable_accumulation) { + vec3 sum = imageLoad(accumulation_image, pixel_coords).rgb; + sum += sample_color; + imageStore(accumulation_image, pixel_coords, vec4(sum, 1.0)); - color = clamp(color, vec3(0.0), vec3(10.0)); - - if (u_enable_accumulation && u_frame_count > 0u) { - vec3 accumulated = imageLoad(accumulation_image, pixel_coords).rgb; - float w = 1.0 / float(u_frame_count + 1u); - color = mix(accumulated, color, w); - } - - imageStore(accumulation_image, pixel_coords, vec4(color, 1.0)); - imageStore(output_image, pixel_coords, vec4(color, 1.0)); + float inv_n = 1.0 / float(u_frame_count + 1u); // u_frame_count is "sample index" + vec3 avg = sum * inv_n; + imageStore(output_image, pixel_coords, vec4(avg, 1.0)); + } else { + imageStore(output_image, pixel_coords, vec4(sample_color, 1.0)); + } } diff --git a/src/core/denoiser.cpp b/src/core/denoiser.cpp new file mode 100644 index 0000000..d96326d --- /dev/null +++ b/src/core/denoiser.cpp @@ -0,0 +1,93 @@ +#include "core/denoiser.h" +#include "basic/constants.h" +#include "utils/logger.h" +#include + +namespace are { + +Denoiser::Denoiser(uint width, uint height) + : width_(width) + , height_(height) + , output_texture_(INVALID_HANDLE) + , initialized_(false) { +} + +Denoiser::~Denoiser() { + release(); +} + +bool Denoiser::initialize(const std::shared_ptr& shader) { + if (initialized_) return true; + + if (!shader || !shader->is_valid()) { + Logger::error("Invalid denoise shader"); + return false; + } + + shader_ = shader; + create_output_texture_(); + + initialized_ = true; + Logger::info("Denoiser initialized"); + return true; +} + +void Denoiser::release() { + if (!initialized_) return; + + shader_.reset(); + + if (output_texture_ != INVALID_HANDLE) { + glDeleteTextures(1, &output_texture_); + output_texture_ = INVALID_HANDLE; + } + + initialized_ = false; +} + +void Denoiser::resize(uint width, uint height) { + if (width == width_ && height == height_) return; + width_ = width; + height_ = height; + + if (!initialized_) return; + + if (output_texture_ != INVALID_HANDLE) { + glDeleteTextures(1, &output_texture_); + output_texture_ = INVALID_HANDLE; + } + create_output_texture_(); +} + +TextureHandle Denoiser::denoise(TextureHandle input_texture, int radius) { + if (!initialized_) return input_texture; + + radius = (radius < 0) ? 0 : radius; + + shader_->use(); + + glBindImageTexture(0, input_texture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); + glBindImageTexture(1, output_texture_, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); + + shader_->set_int("u_radius", radius); + + uint groups_x = (width_ + COMPUTE_GROUP_SIZE_X - 1) / COMPUTE_GROUP_SIZE_X; + uint groups_y = (height_ + COMPUTE_GROUP_SIZE_Y - 1) / COMPUTE_GROUP_SIZE_Y; + glDispatchCompute(groups_x, groups_y, 1); + + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + return output_texture_; +} + +void Denoiser::create_output_texture_() { + glGenTextures(1, &output_texture_); + glBindTexture(GL_TEXTURE_2D, output_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); +} + +} // namespace are diff --git a/src/core/raytracer.cpp b/src/core/raytracer.cpp index 8568a99..92c08bf 100644 --- a/src/core/raytracer.cpp +++ b/src/core/raytracer.cpp @@ -184,14 +184,48 @@ void RayTracer::trace(const Scene &scene, const GBuffer &gbuffer, TextureHandle 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); + const uint spp = std::max(config_.samples_per_pixel_, 1u); - // Memory barrier - glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + // We interpret frame_count_ as "accumulated sample count" + for (uint i = 0; i < spp; ++i) { + compute_shader_->use(); - // Increment frame count for accumulation - if (config_.enable_accumulation_) { - frame_count_++; + // 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); + compute_shader_->set_uint("u_bvh_node_count", 0u); + } + + // Set uniforms (u_frame_count is sample index) + compute_shader_->set_uint("u_frame_count", frame_count_); + compute_shader_->set_uint("u_samples_per_pixel", 1u); // shader does 1 sample per dispatch now + 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_); + + // Camera + const Camera& camera = scene.get_camera(); + Mat4 inv_vp = glm::inverse(camera.get_view_projection_matrix()); + compute_shader_->set_mat4("u_inv_view_projection", inv_vp); + + glDispatchCompute(num_groups_x, num_groups_y, 1); + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + if (config_.enable_accumulation_) { + frame_count_++; + } } } diff --git a/src/core/renderer.cpp b/src/core/renderer.cpp index bb1d2ec..021024c 100644 --- a/src/core/renderer.cpp +++ b/src/core/renderer.cpp @@ -66,7 +66,15 @@ bool Renderer::initialize() { Logger::error("Failed to initialize screen blit"); return false; } - + + // Initialize denoiser + denoiser_ = std::make_unique(config_.width_, config_.height_); + const auto& denoise_shader = shader_manager_->get_denoise_shader(); + if (!denoiser_->initialize(denoise_shader)) { + Logger::error("Failed to initialize denoiser"); + return false; + } + initialized_ = true; Logger::info("Aurora Rendering Engine initialized successfully"); return true; @@ -98,6 +106,11 @@ void Renderer::shutdown() { shader_manager_.reset(); } + if(denoiser_) { + denoiser_->release(); + denoiser_.reset(); + } + initialized_ = false; Logger::info("Aurora Rendering Engine shut down"); } @@ -148,11 +161,18 @@ RenderStats Renderer::render(const Scene& scene, TextureHandle output_texture) { auto raytrace_end = std::chrono::high_resolution_clock::now(); stats.raytrace_time_ms_ = std::chrono::duration(raytrace_end - raytrace_start).count(); - // Phase 3: Blit to screen if output is default framebuffer - if (created_temp_texture && output_texture == 0) { - screen_blit_->blit_fullscreen(rt_output); - glDeleteTextures(1, &rt_output); - } + // Phase 3: Denoise texture + TextureHandle final_output = rt_output; + + if (denoiser_) { + final_output = denoiser_->denoise(rt_output, 1); // radius=1 => 3x3 + } + + // Phase 4: Blit to screen if output is default framebuffer + if (created_temp_texture && output_texture == 0) { + screen_blit_->blit_fullscreen(final_output); + glDeleteTextures(1, &rt_output); + } // Calculate total frame time auto end_time = std::chrono::high_resolution_clock::now(); @@ -182,6 +202,7 @@ void Renderer::resize(uint width, uint height) { if (initialized_) { gbuffer_->resize(width, height); raytracer_->resize(width, height); + denoiser_->resize(width, height); Logger::info("Renderer resized to " + std::to_string(width) + "x" + std::to_string(height)); } diff --git a/src/core/shader_manager.cpp b/src/core/shader_manager.cpp index 6c9c48f..f18c0d9 100644 --- a/src/core/shader_manager.cpp +++ b/src/core/shader_manager.cpp @@ -39,6 +39,7 @@ void ShaderManager::release() { gbuffer_shader_.reset(); raytracing_shader_.reset(); + denoise_shader_.reset(); initialized_ = false; Logger::info("ShaderManager released"); @@ -108,6 +109,15 @@ bool ShaderManager::load_builtin_shaders_() { shader_cache_["raytracing"] = raytracing_shader_; Logger::info("Ray tracing shader loaded successfully"); + Logger::info("Loading denoise compute shader..."); + denoise_shader_ = std::make_shared(); + if (!denoise_shader_->load_compute("shaders/denoiser.comp")) { + Logger::error("Failed to load denoise shader"); + return false; + } + shader_cache_["denoise"] = denoise_shader_; + Logger::info("Denoise shader loaded successfully"); + return true; }