Browse Source

Merge pull request #66756 from BastiaanOlij/fix_ssr

Fixing artifacts in SSR
Rémi Verschelde 2 years ago
parent
commit
17c62a692e

+ 31 - 7
servers/rendering/renderer_rd/effects/ss_effects.cpp

@@ -1457,6 +1457,17 @@ void SSEffects::ssr_set_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_q
 
 void SSEffects::ssr_allocate_buffers(SSRRenderBuffers &p_ssr_buffers, const RenderingDevice::DataFormat p_color_format, const Size2i &p_screen_size, const uint32_t p_view_count) {
 	// As we are processing one view at a time, we can reuse buffers, only our output needs to have layers for each view.
+	if (p_ssr_buffers.size != p_screen_size || p_ssr_buffers.roughness_quality != ssr_roughness_quality) {
+		ssr_free(p_ssr_buffers);
+	}
+
+	if (p_ssr_buffers.output.is_valid()) {
+		// already allocated
+		return;
+	}
+
+	p_ssr_buffers.size = p_screen_size;
+	p_ssr_buffers.roughness_quality = ssr_roughness_quality;
 
 	if (p_ssr_buffers.depth_scaled.is_null()) {
 		RD::TextureFormat tf;
@@ -1581,8 +1592,13 @@ void SSEffects::screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const R
 			RD::Uniform u_normal_roughness(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, Vector<RID>({ default_sampler, p_normal_roughness_slices[v] }));
 			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_depth, u_normal_roughness), 1);
 
-			RD::Uniform u_output_blur(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.output_slices[v] }));
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_output_blur), 2);
+			if (ssr_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED) {
+				RD::Uniform u_output(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.output_slices[v] }));
+				RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_output), 2);
+			} else {
+				RD::Uniform u_intermediate(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.intermediate }));
+				RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_intermediate), 2);
+			}
 
 			RD::Uniform u_scale_depth(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.depth_scaled }));
 			RD::Uniform u_scale_normal(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_ssr_buffers.normal_scaled }));
@@ -1624,17 +1640,25 @@ void SSEffects::screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const R
 			RD::Uniform u_scene_data(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 0, ssr.ubo);
 			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 4, u_scene_data), 4);
 
-			RD::Uniform u_output_blur(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.output_slices[v] }));
-			RD::Uniform u_scale_depth(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_ssr_buffers.depth_scaled }));
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_output_blur, u_scale_depth), 0);
-
 			if (ssr_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED) {
+				// read from output slices (our scale wrote into these)
+				RD::Uniform u_output(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.output_slices[v] }));
+				RD::Uniform u_scale_depth(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_ssr_buffers.depth_scaled }));
+				RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_output, u_scale_depth), 0);
+
+				// write to intermediate (our roughness pass will output into output slices)
 				RD::Uniform u_intermediate(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.intermediate }));
 				RD::Uniform u_blur_radius(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_ssr_buffers.blur_radius[0] }));
 				RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_intermediate, u_blur_radius), 1);
 			} else {
+				// read from intermediate (our scale wrote into these)
 				RD::Uniform u_intermediate(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.intermediate }));
-				RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_intermediate), 1);
+				RD::Uniform u_scale_depth(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_ssr_buffers.depth_scaled }));
+				RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_intermediate, u_scale_depth), 0);
+
+				// We are not performing our blur so go directly to output.
+				RD::Uniform u_output(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.output_slices[v] }));
+				RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_output), 1);
 			}
 
 			RD::Uniform u_scale_normal(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.normal_scaled }));

+ 3 - 0
servers/rendering/renderer_rd/effects/ss_effects.h

@@ -148,6 +148,9 @@ public:
 	void ssr_set_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality);
 
 	struct SSRRenderBuffers {
+		Size2i size;
+		RenderingServer::EnvironmentSSRRoughnessQuality roughness_quality = RenderingServer::ENV_SSR_ROUGHNESS_QUALITY_DISABLED;
+
 		RID normal_scaled;
 		RID depth_scaled;
 		RID blur_radius[2];

+ 2 - 3
servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp

@@ -1500,9 +1500,8 @@ void RenderForwardClustered::_process_ssr(Ref<RenderSceneBuffersRD> p_render_buf
 	ERR_FAIL_COND(!environment_get_ssr_enabled(p_environment));
 
 	Size2i half_size = Size2i(internal_size.x / 2, internal_size.y / 2);
-	if (rb_data->ss_effects_data.ssr.output.is_null()) {
-		ss_effects->ssr_allocate_buffers(rb_data->ss_effects_data.ssr, _render_buffers_get_color_format(), half_size, view_count);
-	}
+	ss_effects->ssr_allocate_buffers(rb_data->ss_effects_data.ssr, _render_buffers_get_color_format(), half_size, view_count);
+
 	RID texture_slices[RendererSceneRender::MAX_RENDER_VIEWS];
 	RID depth_slices[RendererSceneRender::MAX_RENDER_VIEWS];
 	for (uint32_t v = 0; v < view_count; v++) {

+ 25 - 9
servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl

@@ -121,7 +121,7 @@ void main() {
 
 	// clip z and w advance to line advance
 	vec2 line_advance = normalize(line_dir); // down to pixel
-	float step_size = length(line_advance) / length(line_dir);
+	float step_size = 1.0 / length(line_dir);
 	float z_advance = z_dir * step_size; // adapt z advance to line advance
 	float w_advance = w_dir * step_size; // adapt w advance to line advance
 
@@ -139,6 +139,14 @@ void main() {
 	float depth;
 	vec2 prev_pos = pos;
 
+	if (ivec2(pos + line_advance - 0.5) == ssC) {
+		// It is possible for rounding to cause our first pixel to check to be the pixel we're reflecting.
+		// Make sure we skip it
+		pos += line_advance;
+		z += z_advance;
+		w += w_advance;
+	}
+
 	bool found = false;
 
 	float steps_taken = 0.0;
@@ -149,8 +157,8 @@ void main() {
 		w += w_advance;
 
 		// convert to linear depth
-
-		depth = imageLoad(source_depth, ivec2(pos - 0.5)).r;
+		ivec2 test_pos = ivec2(pos - 0.5);
+		depth = imageLoad(source_depth, test_pos).r;
 		if (sc_multiview) {
 			depth = depth * 2.0 - 1.0;
 			depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near - depth * (params.camera_z_far - params.camera_z_near));
@@ -161,13 +169,21 @@ void main() {
 		z_to = z / w;
 
 		if (depth > z_to) {
-			// if depth was surpassed
-			if (depth <= max(z_to, z_from) + params.depth_tolerance && -depth < params.camera_z_far * 0.95) {
-				// check the depth tolerance and far clip
-				// check that normal is valid
-				found = true;
+			// Test if our ray is hitting the "right" side of the surface, if not we're likely self reflecting and should skip.
+			vec4 test_normal_roughness = imageLoad(source_normal_roughness, test_pos);
+			vec3 test_normal = test_normal_roughness.xyz * 2.0 - 1.0;
+			test_normal = normalize(test_normal);
+			test_normal.y = -test_normal.y; //because this code reads flipped
+
+			if (dot(ray_dir, test_normal) < 0.001) {
+				// if depth was surpassed
+				if (depth <= max(z_to, z_from) + params.depth_tolerance && -depth < params.camera_z_far * 0.95) {
+					// check the depth tolerance and far clip
+					// check that normal is valid
+					found = true;
+				}
+				break;
 			}
-			break;
 		}
 
 		steps_taken += 1.0;