From 35d1c70bc1747215442daab017b7d15019d4ca84 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Fri, 22 Dec 2023 20:20:27 +0100 Subject: [PATCH] Use dithering when performing SSR roughness to improve quality This works best when TAA or FSR2 is enabled to jitter the result every frame, letting TAA accumulation do its work. --- .../renderer_rd/effects/ss_effects.cpp | 2 ++ .../renderer_rd/effects/ss_effects.h | 6 +++++ .../screen_space_reflection_filter.glsl | 26 +++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/servers/rendering/renderer_rd/effects/ss_effects.cpp b/servers/rendering/renderer_rd/effects/ss_effects.cpp index 36a2470c7b7..8655ab3dff3 100644 --- a/servers/rendering/renderer_rd/effects/ss_effects.cpp +++ b/servers/rendering/renderer_rd/effects/ss_effects.cpp @@ -1525,6 +1525,8 @@ void SSEffects::screen_space_reflection(Ref p_render_buffe push_constant.view_index = v; push_constant.orthogonal = p_projections[v].is_orthogonal(); push_constant.edge_tolerance = Math::sin(Math::deg_to_rad(15.0)); + constexpr int taa_phase_count = 16; + push_constant.taa_frame_count = (RSG::rasterizer->get_frame_number() % taa_phase_count) / float(taa_phase_count); push_constant.proj_info[0] = -2.0f / (p_ssr_buffers.size.width * p_projections[v].columns[0][0]); push_constant.proj_info[1] = -2.0f / (p_ssr_buffers.size.height * p_projections[v].columns[1][1]); push_constant.proj_info[2] = (1.0f - p_projections[v].columns[0][2]) / p_projections[v].columns[0][0]; diff --git a/servers/rendering/renderer_rd/effects/ss_effects.h b/servers/rendering/renderer_rd/effects/ss_effects.h index 8585277e194..1f5c5f06d0c 100644 --- a/servers/rendering/renderer_rd/effects/ss_effects.h +++ b/servers/rendering/renderer_rd/effects/ss_effects.h @@ -490,6 +490,12 @@ private: int32_t screen_size[2]; // 8 - 40 uint32_t vertical; // 4 - 44 uint32_t steps; // 4 - 48 + + // Used to add break up samples over multiple frames. Value is an integer from 0 to taa_phase_count -1. + float taa_frame_count; // 4 - 52 + uint32_t pad0; // 4 - 56 + uint32_t pad1; // 4 - 60 + uint32_t pad2; // 4 - 64 }; enum SSRReflectionMode { diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_filter.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_filter.glsl index a63d60e0b23..2c8c3dbffa5 100644 --- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_filter.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_filter.glsl @@ -27,6 +27,12 @@ layout(push_constant, std430) uniform Params { ivec2 screen_size; bool vertical; uint steps; + + // Used to add break up samples over multiple frames. Value is an integer from 0 to taa_phase_count -1. + float taa_frame_count; + uint pad0; + uint pad1; + uint pad2; } params; @@ -66,6 +72,13 @@ float gauss_weight(float p_val) { #define M_PI 3.14159265359 +// Interleaved Gradient Noise +// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare +float quick_hash(vec2 pos) { + const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f); + return fract(magic.z * fract(dot(pos, magic.xy))); +} + void do_filter(inout vec4 accum, inout float accum_radius, inout float divisor, ivec2 texcoord, ivec2 increment, vec3 p_pos, vec3 normal, float p_limit_radius) { for (int i = 1; i < params.steps; i++) { float d = float(i * params.increment); @@ -118,10 +131,19 @@ void main() { float divisor = gauss_table[0]; accum *= divisor; accum_radius *= divisor; + + // The intent is to spread the increment on Low quality (3) to be between 1 and 5. + // On Medium quality (2), it would be between 1 and 3. + // + // See https://github.com/godotengine/godot/blob/ad2722f48180c21147a31ee59e15722a237a7445/servers/rendering/renderer_rd/effects/ss_effects.cpp#L1535-L1544 + // for values of `params.increment` depending on quality. + int increment_offset = params.increment - 1; + int taa_jitter = int(quick_hash(vec2(ssC.xy) + vec2(params.taa_frame_count * 5.0, params.taa_frame_count * 2.0)) * (params.increment + increment_offset) - increment_offset); + #ifdef VERTICAL_PASS - ivec2 direction = ivec2(0, params.increment); + ivec2 direction = ivec2(0, params.increment + taa_jitter); #else - ivec2 direction = ivec2(params.increment, 0); + ivec2 direction = ivec2(params.increment + taa_jitter, 0); #endif float depth = imageLoad(source_depth, ssC).r; vec3 pos = reconstructCSPosition(vec2(ssC.xy) + 0.5, depth);