소스 검색

Merge pull request #107254 from passivestar/shadow-unleak

Fix LightmapGI shadow leaks
Rémi Verschelde 1 개월 전
부모
커밋
09ed9d4a93
3개의 변경된 파일39개의 추가작업 그리고 12개의 파일을 삭제
  1. 21 5
      modules/lightmapper_rd/lightmapper_rd.cpp
  2. 2 2
      modules/lightmapper_rd/lightmapper_rd.h
  3. 16 5
      modules/lightmapper_rd/lm_compute.glsl

+ 21 - 5
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<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;

+ 2 - 2
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<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);

+ 16 - 5
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;
 
@@ -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;