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.
This commit is contained in:
Hugo Locurcio 2023-12-22 20:20:27 +01:00
parent 506d6e427a
commit 35d1c70bc1
No known key found for this signature in database
GPG key ID: 39E8F8BE30B0A49C
3 changed files with 32 additions and 2 deletions

View file

@ -1525,6 +1525,8 @@ void SSEffects::screen_space_reflection(Ref<RenderSceneBuffersRD> 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];

View file

@ -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 {

View file

@ -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);