Merge pull request #107254 from passivestar/shadow-unleak

Fix LightmapGI shadow leaks
This commit is contained in:
Rémi Verschelde 2025-06-14 00:00:12 +02:00
commit 09ed9d4a93
No known key found for this signature in database
GPG key ID: C3336907360768E1
3 changed files with 39 additions and 12 deletions

View file

@ -979,7 +979,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID
return BAKE_OK;
}
LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &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<RDShaderFile> &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, Ref<RDSh
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 3;
u.append_id(p_unocclude_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
u.binding = 4;
u.append_id(denoise_params_buffer);
uniforms.push_back(u);
}
@ -1622,6 +1629,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
}
PushConstant push_constant;
push_constant.denoiser_range = p_use_denoiser ? p_denoiser_range : 1.0;
/* UNOCCLUDE */
{
@ -1638,7 +1646,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 1;
u.append_id(unocclude_tex); //will be unused
u.append_id(unocclude_tex);
uniforms.push_back(u);
}
}
@ -1659,6 +1667,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
rd->compute_list_end(); //done
}
#ifdef DEBUG_TEXTURES
for (int i = 0; i < atlas_slices; i++) {
Vector<uint8_t> s = rd->texture_get_data(unocclude_tex, i);
Ref<Image> 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;

View file

@ -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<Ref<Image>> 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<int> 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<RDShaderFile> &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<RDShaderFile> &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<RDShaderFile> &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<RDShaderFile> &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);

View file

@ -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;
@ -1011,13 +1013,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;
@ -1028,6 +1033,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
@ -1211,6 +1217,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++) {
@ -1252,12 +1259,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;