From 219035c5ea06d84ff76a021a94fa1ab95f0998ee Mon Sep 17 00:00:00 2001 From: passivestar <60579014+passivestar@users.noreply.github.com> Date: Sat, 7 Jun 2025 13:21:06 +0400 Subject: [PATCH] Fix LightmapGI shadow leaks --- modules/lightmapper_rd/lightmapper_rd.cpp | 26 ++++++++++++++++++----- modules/lightmapper_rd/lightmapper_rd.h | 4 ++-- modules/lightmapper_rd/lm_compute.glsl | 21 +++++++++++++----- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 8283748487..8f81fe7c0a 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -979,7 +979,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID return BAKE_OK; } -LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, int p_denoiser_range, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function, void *p_bake_userdata) { +LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, RID p_unocclude_tex, float p_denoiser_strength, int p_denoiser_range, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function, void *p_bake_userdata) { RID denoise_params_buffer = p_rd->uniform_buffer_create(sizeof(DenoiseParams)); DenoiseParams denoise_params; denoise_params.spatial_bandwidth = 5.0f; @@ -1000,8 +1000,15 @@ LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Refcompute_list_end(); //done } +#ifdef DEBUG_TEXTURES + for (int i = 0; i < atlas_slices; i++) { + Vector s = rd->texture_get_data(unocclude_tex, i); + Ref img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAF, s); + img->save_exr("res://1_unocclude_" + itos(i) + ".exr", false); + } +#endif + if (p_step_function) { if (p_step_function(0.5, RTR("Plot direct lighting"), p_bake_userdata, true)) { FREE_TEXTURES @@ -2083,7 +2099,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d } else { // JNLM (built-in). SWAP(light_accum_tex, light_accum_tex2); - error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, normal_tex, light_accum_tex, p_denoiser_strength, p_denoiser_range, atlas_size, atlas_slices, p_bake_sh, p_step_function, p_bake_userdata); + error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, normal_tex, light_accum_tex, unocclude_tex, p_denoiser_strength, p_denoiser_range, atlas_size, atlas_slices, p_bake_sh, p_step_function, p_bake_userdata); } if (unlikely(error != BAKE_OK)) { return error; @@ -2098,7 +2114,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d } else { // JNLM (built-in). SWAP(shadowmask_tex, shadowmask_tex2); - error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, shadowmask_tex2, normal_tex, shadowmask_tex, p_denoiser_strength, p_denoiser_range, atlas_size, atlas_slices, false, p_step_function, p_bake_userdata); + error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, shadowmask_tex2, normal_tex, shadowmask_tex, unocclude_tex, p_denoiser_strength, p_denoiser_range, atlas_size, atlas_slices, false, p_step_function, p_bake_userdata); } if (unlikely(error != BAKE_OK)) { return error; diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h index b962303a2b..5a1e5a81f2 100644 --- a/modules/lightmapper_rd/lightmapper_rd.h +++ b/modules/lightmapper_rd/lightmapper_rd.h @@ -261,7 +261,7 @@ class LightmapperRD : public Lightmapper { uint32_t ray_to = 0; uint32_t region_ofs[2] = {}; uint32_t probe_count = 0; - uint32_t pad = 0; + uint32_t denoiser_range = 0; }; Vector> lightmap_textures; @@ -289,7 +289,7 @@ class LightmapperRD : public Lightmapper { void _raster_geometry(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, int grid_size, AABB bounds, float p_bias, Vector slice_triangle_count, RID position_tex, RID unocclude_tex, RID normal_tex, RID raster_depth_buffer, RID rasterize_shader, RID raster_base_uniform); BakeError _dilate(RenderingDevice *rd, Ref &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices); - BakeError _denoise(RenderingDevice *p_rd, Ref &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, int p_denoiser_range, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function, void *p_bake_userdata); + BakeError _denoise(RenderingDevice *p_rd, Ref &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, RID p_unocclude_tex, float p_denoiser_strength, int p_denoiser_range, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function, void *p_bake_userdata); BakeError _pack_l1(RenderingDevice *rd, Ref &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices); Error _store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_index, const Size2i &p_atlas_size, const String &p_name, bool p_shadowmask); diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl index 1c0f69a867..8575bd51a6 100644 --- a/modules/lightmapper_rd/lm_compute.glsl +++ b/modules/lightmapper_rd/lm_compute.glsl @@ -46,7 +46,7 @@ layout(set = 1, binding = 2) uniform texture2D environment; #ifdef MODE_UNOCCLUDE layout(rgba32f, set = 1, binding = 0) uniform restrict image2DArray position; -layout(rgba32f, set = 1, binding = 1) uniform restrict readonly image2DArray unocclude; +layout(rgba32f, set = 1, binding = 1) uniform restrict image2DArray unocclude; #endif @@ -73,7 +73,8 @@ layout(set = 1, binding = 1) uniform texture2DArray source_light; #ifdef MODE_DENOISE layout(set = 1, binding = 2) uniform texture2DArray source_normal; -layout(set = 1, binding = 3) uniform DenoiseParams { +layout(set = 1, binding = 3) uniform texture2DArray unocclude_mask; +layout(set = 1, binding = 4) uniform DenoiseParams { float spatial_bandwidth; float light_bandwidth; float albedo_bandwidth; @@ -93,6 +94,7 @@ layout(push_constant, std430) uniform Params { ivec2 region_ofs; uint probe_count; + uint denoiser_range; } params; @@ -995,13 +997,16 @@ void main() { vec3 rays[4] = vec3[](tangent, bitangent, -tangent, -bitangent); float min_d = 1e20; + float unocclude_mask = 0.0; + for (int i = 0; i < 4; i++) { - vec3 ray_to = base_pos + rays[i] * texel_size; + vec3 ray_to = base_pos + rays[i] * texel_size * params.denoiser_range; float d; vec3 norm; if (trace_ray_closest_hit_distance(base_pos, ray_to, d, norm) == RAY_BACK) { - if (d < min_d) { + unocclude_mask = 1.0; + if (d <= texel_size && d < min_d) { // This bias needs to be greater than the regular bias, because otherwise later, rays will go the other side when pointing back. vertex_pos = base_pos + rays[i] * d + norm * bake_params.bias * 10.0; min_d = d; @@ -1012,6 +1017,7 @@ void main() { position_alpha.xyz = vertex_pos; imageStore(position, ivec3(atlas_pos, params.atlas_slice), position_alpha); + imageStore(unocclude, ivec3(atlas_pos, params.atlas_slice), vec4(unocclude_mask, 0, 0, 0)); #endif @@ -1195,6 +1201,7 @@ void main() { vec3 search_rgb = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(search_pos, lightmap_slice), 0).rgb; vec3 search_albedo = texelFetch(sampler2DArray(albedo_tex, linear_sampler), ivec3(search_pos, params.atlas_slice), 0).rgb; vec3 search_normal = texelFetch(sampler2DArray(source_normal, linear_sampler), ivec3(search_pos, params.atlas_slice), 0).xyz; + float search_occlusion = texelFetch(sampler2DArray(unocclude_mask, linear_sampler), ivec3(search_pos, params.atlas_slice), 0).r; float patch_square_dist = 0.0f; for (int offset_y = -HALF_PATCH_WINDOW; offset_y <= HALF_PATCH_WINDOW; offset_y++) { for (int offset_x = -HALF_PATCH_WINDOW; offset_x <= HALF_PATCH_WINDOW; offset_x++) { @@ -1236,12 +1243,16 @@ void main() { float normal_square_dist = dot(normal_delta, normal_delta); weight *= exp(-normal_square_dist / TWO_SIGMA_NORMAL_SQUARE); + // Weight with occlusion. + weight *= 1.0 - search_occlusion; + denoised_rgb += weight * search_rgb; sum_weights += weight; } } - denoised_rgb /= sum_weights; + // Avoid division by zero if no weights were accumulated. + denoised_rgb = sum_weights > EPSILON ? denoised_rgb / sum_weights : input_rgb; } else { // Ignore pixels where the normal is empty, just copy the light color. denoised_rgb = input_light.rgb;