From aaa6de330cf0202d303bd76d33ec75f58a517835 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Thu, 1 Feb 2024 10:36:34 -0500 Subject: [PATCH] =?UTF-8?q?vk:=20rt:=20add=20experimental=20second=20?= =?UTF-8?q?=C3=81-Trous=20pass=20for=20indirect=20diffuse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now indirect diffuse channel gets blurred with 2 Á-Trous passes. It mostly follows the paper, except for Σd_i, which gives very bad viusal artifacts. --- ref/vk/TODO.md | 15 ++-- ref/vk/shaders/atrous.glsl | 48 +++++++++++ ref/vk/shaders/denoiser.comp | 90 +++++++------------- ref/vk/shaders/indirect_diffuse_atrous1.comp | 68 +++++++++++++++ ref/vk/shaders/rt.json | 3 + 5 files changed, 162 insertions(+), 62 deletions(-) create mode 100644 ref/vk/shaders/atrous.glsl create mode 100644 ref/vk/shaders/indirect_diffuse_atrous1.comp diff --git a/ref/vk/TODO.md b/ref/vk/TODO.md index 3663aa0b6a..08136d9cf6 100644 --- a/ref/vk/TODO.md +++ b/ref/vk/TODO.md @@ -1,19 +1,24 @@ ## Next +- [ ] fix no-hit bounce absent legacy blending - [ ] add white furnace render test +- [ ] add new channels to render tests - [ ] temporal glitches with dontBlurSamples() and ATrous -- [ ] fix no-hit bounce absent legacy blending -- [ ] -vkdbgprintf or something +- [ ] -vkdbgprintf or something for explicitly turning shader debug printfs - [ ] performance profiling and comparison +## 2024-02-01 E371 +- [x] tune A-Trous step widths for different channels + - [x] multiple passes -- core of the paper lol + +# Previously ## 2024-01-29 E370 - [x] bounce > 1 brighness - [ ] tune A-Trous step widths for different channels - [x] tune parameters - [x] "cone width" - - [ ] different parameters/radii for different channels - - [ ] multiple passes + - [x] different parameters/radii for different channels + - [ ] multiple passes -- core of the paper lol -# Previously ## 2024-01-26 E369 - [x] white furnace test - [x] do it using display_only mode diff --git a/ref/vk/shaders/atrous.glsl b/ref/vk/shaders/atrous.glsl new file mode 100644 index 0000000000..ca134adb51 --- /dev/null +++ b/ref/vk/shaders/atrous.glsl @@ -0,0 +1,48 @@ +#ifndef ATROUS_CONSTS_DECLARED +#define ATROUS_CONSTS_DECLARED + +#include "utils.glsl" + +// https://jo.dreggn.org/home/2010_atrous.pdf +// https://www.shadertoy.com/view/ldKBzG +#define ATROUS_KERNEL_WIDTH 5 +#define ATROUS_KERNEL_HALF 2 +const float kATrousKernel[ATROUS_KERNEL_WIDTH] = { 1./16., 1./4., 3./8., 1./4., 1./16. }; + + +// Depends on: +// - image2D normals_gs +// - image2D position_t +float aTrousSampleWeigth(const ivec2 res, const ivec2 pix, vec3 pos, vec3 shading_normal, ivec2 offset, int step_width, int pix_scale, float phi_normal, float phi_pos, out ivec2 p) { + const float x_kernel = kATrousKernel[offset.x]; + const float y_kernel = kATrousKernel[offset.y]; + + const float inv_step_width_sq = 1. / float(step_width * step_width); + p = pix + (offset - ivec2(ATROUS_KERNEL_HALF)) * step_width; + const ivec2 p_scaled = p * pix_scale; + + if (any(greaterThanEqual(p_scaled, res)) || any(lessThan(p_scaled, ivec2(0)))) { + return 0.; + } + + // Weight normals + const vec4 ngs = imageLoad(normals_gs, p_scaled); + const vec3 sample_shading_normal = normalDecode(ngs.zw); + + // TODO should we go geometry_normal too? + const vec3 sn_diff = sample_shading_normal - shading_normal; + const float sn_dist2 = max(dot(sn_diff,sn_diff) * inv_step_width_sq, 0.); + const float weight_sn = min(exp(-(sn_dist2)/phi_normal), 1.0); + + // Weight positions + const vec3 sample_position = imageLoad(position_t, p_scaled).xyz; + const vec3 p_diff = sample_position - pos; + //Original paper: const float p_dist2 = dot(p_diff, p_diff); + const float p_dist2 = max(dot(p_diff,p_diff) * inv_step_width_sq, 0.); + const float weight_pos = min(exp(-(p_dist2)/phi_pos),1.0); + + const float weight = (weight_pos * weight_sn) * x_kernel * y_kernel; + return weight; +} + +#endif // ifndef ATROUS_CONSTS_DECLARED diff --git a/ref/vk/shaders/denoiser.comp b/ref/vk/shaders/denoiser.comp index a13347cc90..c3ec77813b 100644 --- a/ref/vk/shaders/denoiser.comp +++ b/ref/vk/shaders/denoiser.comp @@ -28,24 +28,26 @@ layout(set = 0, binding = 10, rgba32f) uniform readonly image2D geometry_prev_po layout(set = 0, binding = 11) uniform UBO { UniformBuffer ubo; } ubo; layout(set = 0, binding = 12, rgba16f) uniform readonly image2D indirect_diffuse; -layout(set = 0, binding = 13, rgba16f) uniform readonly image2D indirect_specular; +layout(set = 0, binding = 13, rgba16f) uniform readonly image2D indirect_diffuse_atrous1; +layout(set = 0, binding = 14, rgba16f) uniform readonly image2D indirect_specular; -layout(set = 0, binding = 14, rgba16f) uniform image2D out_temporal_diffuse; -layout(set = 0, binding = 15, rgba16f) uniform image2D prev_temporal_diffuse; +layout(set = 0, binding = 15, rgba16f) uniform image2D out_temporal_diffuse; +layout(set = 0, binding = 16, rgba16f) uniform image2D prev_temporal_diffuse; -layout(set = 0, binding = 16, rgba16f) uniform image2D out_temporal_specular; -layout(set = 0, binding = 17, rgba16f) uniform image2D prev_temporal_specular; +layout(set = 0, binding = 17, rgba16f) uniform image2D out_temporal_specular; +layout(set = 0, binding = 18, rgba16f) uniform image2D prev_temporal_specular; //#define DEBUG_NOISE #ifdef DEBUG_NOISE -layout(set = 0, binding = 18) uniform sampler3D blue_noise_texture; +layout(set = 0, binding = 19) uniform sampler3D blue_noise_texture; #include "bluenoise.glsl" #endif -layout(set = 0, binding = 19, rgba16f) uniform readonly image2D legacy_blend; +layout(set = 0, binding = 20, rgba16f) uniform readonly image2D legacy_blend; -//layout(set = 0, binding = 20) uniform sampler2D textures[MAX_TEXTURES]; +//layout(set = 0, binding = 21) uniform sampler2D textures[MAX_TEXTURES]; +#include "atrous.glsl" const int INDIRECT_SCALE = 2; @@ -146,43 +148,6 @@ Components boxBlurSamples(ivec2 res, ivec2 pix) { return c; } -// https://jo.dreggn.org/home/2010_atrous.pdf -// https://www.shadertoy.com/view/ldKBzG -#define ATROUS_KERNEL_WIDTH 5 -#define ATROUS_KERNEL_HALF 2 -const float kATrousKernel[ATROUS_KERNEL_WIDTH] = { 1./16., 1./4., 3./8., 1./4., 1./16. }; - -float aTrousSampleWeigth(const ivec2 res, const ivec2 pix, vec3 pos, vec3 shading_normal, ivec2 offset, int step_width, float phi_normal, float phi_pos, out ivec2 p) { - const float x_kernel = kATrousKernel[offset.x]; - const float y_kernel = kATrousKernel[offset.y]; - - const float inv_step_width_sq = 1. / float(step_width * step_width); - p = pix + (offset - ivec2(ATROUS_KERNEL_HALF)) * step_width; - - if (any(greaterThanEqual(p, res)) || any(lessThan(p, ivec2(0)))) { - return 0.; - } - - // Weight normals - vec3 sample_geometry_normal, sample_shading_normal; - readNormals(p, sample_geometry_normal, sample_shading_normal); - - // TODO should we go geometry_normal too? - const vec3 sn_diff = sample_shading_normal - shading_normal; - const float sn_dist2 = max(dot(sn_diff,sn_diff) * inv_step_width_sq, 0.); - const float weight_sn = min(exp(-(sn_dist2)/phi_normal), 1.0); - - // Weight positions - const vec3 sample_position = imageLoad(position_t, p).xyz; - const vec3 p_diff = sample_position - pos; - //Original paper: const float p_dist2 = dot(p_diff, p_diff); - const float p_dist2 = max(dot(p_diff,p_diff) * inv_step_width_sq, 0.); - const float weight_pos = min(exp(-(p_dist2)/phi_pos),1.0); - - const float weight = (weight_pos * weight_sn) * x_kernel * y_kernel; - return weight; -} - Components blurATrous(const ivec2 res, const ivec2 pix, vec3 pos, vec3 shading_normal, vec3 geometry_normal) { Components c; c.direct_diffuse = c.direct_specular = c.indirect_diffuse = c.indirect_specular = vec3(0.); @@ -191,6 +156,7 @@ Components blurATrous(const ivec2 res, const ivec2 pix, vec3 pos, vec3 shading_n float weight_total_specular = 0.; float weight_total_indirect_diffuse = 0.; float weight_total_indirect_specular = 0.; + vec3 indirect_diffuse_c2 = vec3(0.); const ivec2 res_scaled = res / INDIRECT_SCALE; for (int x = 0; x <= ATROUS_KERNEL_WIDTH; ++x) { for (int y = 0; y <= ATROUS_KERNEL_WIDTH; ++y) { @@ -202,7 +168,7 @@ Components blurATrous(const ivec2 res, const ivec2 pix, vec3 pos, vec3 shading_n const int step_width = 3; ivec2 p; const float weight = aTrousSampleWeigth( - res, pix, pos, shading_normal, offset, step_width, sn_phi, p_phi, p); + res, pix, pos, shading_normal, offset, step_width, 1, sn_phi, p_phi, p); if (weight > 0.) { weight_total_diffuse += weight; @@ -219,7 +185,7 @@ Components blurATrous(const ivec2 res, const ivec2 pix, vec3 pos, vec3 shading_n const int step_width = 1; ivec2 p; const float weight = aTrousSampleWeigth( - res, pix, pos, shading_normal, offset, step_width, sn_phi, p_phi, p); + res, pix, pos, shading_normal, offset, step_width, 1, sn_phi, p_phi, p); if (weight > 0.) { weight_total_specular += weight; @@ -233,17 +199,17 @@ Components blurATrous(const ivec2 res, const ivec2 pix, vec3 pos, vec3 shading_n { const float sn_phi = .5; const float p_phi = 3.; - const int step_width = 3; + const int step_width = 2; ivec2 p; const float weight = aTrousSampleWeigth( - res, pix, pos, shading_normal, offset, step_width, sn_phi, p_phi, p); + res, pix, pos, shading_normal, offset, step_width, 1, sn_phi, p_phi, p); if (weight > 0.) { - const ivec2 p_indirect = p / INDIRECT_SCALE; - const bool do_indirect = all(lessThan(p_indirect, res_scaled)) && all(greaterThanEqual(p_indirect, ivec2(0))); + const ivec2 p_scaled = p / INDIRECT_SCALE; + const bool do_indirect = all(lessThan(p_scaled, res_scaled)); if (do_indirect) { weight_total_indirect_diffuse += weight; - c.indirect_diffuse += imageLoad(indirect_diffuse, p_indirect).rgb * weight; + indirect_diffuse_c2 += imageLoad(indirect_diffuse_atrous1, p_scaled).rgb * weight; } } } @@ -255,14 +221,14 @@ Components blurATrous(const ivec2 res, const ivec2 pix, vec3 pos, vec3 shading_n const int step_width = 1; ivec2 p; const float weight = aTrousSampleWeigth( - res, pix, pos, shading_normal, offset, step_width, sn_phi, p_phi, p); + res, pix, pos, shading_normal, offset, step_width, 1, sn_phi, p_phi, p); if (weight > 0.) { - const ivec2 p_indirect = p / INDIRECT_SCALE; - const bool do_indirect = all(lessThan(p_indirect, res_scaled)) && all(greaterThanEqual(p_indirect, ivec2(0))); + const ivec2 p_scaled = p / INDIRECT_SCALE; + const bool do_indirect = all(lessThan(p_scaled, res_scaled)); if (do_indirect) { weight_total_indirect_specular += weight; - c.indirect_specular += imageLoad(indirect_specular, p_indirect).rgb * weight; + c.indirect_specular += imageLoad(indirect_specular, p_scaled).rgb * weight; } } } @@ -276,8 +242,18 @@ Components blurATrous(const ivec2 res, const ivec2 pix, vec3 pos, vec3 shading_n c.direct_diffuse *= one_over_weight_diffuse; c.direct_specular *= one_over_weight_specular; - c.indirect_diffuse *= one_over_weight_indirect_diffuse; + indirect_diffuse_c2 *= one_over_weight_indirect_diffuse; c.indirect_specular *= one_over_weight_indirect_specular; + + const vec3 indirect_diffuse_c0 = imageLoad(indirect_diffuse, pix / INDIRECT_SCALE).rgb; + const vec3 indirect_diffuse_c1 = imageLoad(indirect_diffuse_atrous1, pix / INDIRECT_SCALE).rgb; + const vec3 d0 = indirect_diffuse_c1 - indirect_diffuse_c0; + const vec3 d1 = indirect_diffuse_c2 - indirect_diffuse_c1; + + // TODO(or not todo): The Á-Trous paper mentions that it should be c2 + d1 + d0, but + // it gives horrible artifacts. Either I'm misreading the paper, or something else is broken here, + // Using just c2 seems fine enough (although still not up to original paper image quality) + c.indirect_diffuse = indirect_diffuse_c2;// + d1 + d0; return c; } diff --git a/ref/vk/shaders/indirect_diffuse_atrous1.comp b/ref/vk/shaders/indirect_diffuse_atrous1.comp new file mode 100644 index 0000000000..84da9424be --- /dev/null +++ b/ref/vk/shaders/indirect_diffuse_atrous1.comp @@ -0,0 +1,68 @@ +#version 460 +//#include "debug.glsl" +//#include "utils.glsl" + +#define GLSL +#include "ray_interop.h" +#undef GLSL + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(set = 0, binding = 0) uniform UBO { UniformBuffer ubo; } ubo; + +layout(set = 0, binding = 1, rgba16f) uniform image2D out_indirect_diffuse_atrous1; + +layout(set = 0, binding = 2, rgba32f) uniform readonly image2D position_t; +layout(set = 0, binding = 3, rgba16f) uniform readonly image2D normals_gs; + +layout(set = 0, binding = 4, rgba16f) uniform readonly image2D indirect_diffuse; + +#include "atrous.glsl" + +const int INDIRECT_SCALE = 2; + +void main() { + const ivec2 res = ubo.ubo.res; + const ivec2 pix = ivec2(gl_GlobalInvocationID); + + const ivec2 res_scaled = res / INDIRECT_SCALE; + if (any(greaterThanEqual(pix, res_scaled))) { + return; + } + + const vec3 pos = imageLoad(position_t, pix * INDIRECT_SCALE).xyz; + const vec3 shading_normal = normalDecode(imageLoad(normals_gs, pix * INDIRECT_SCALE).zw); + + vec3 indiff = vec3(0.); + float weight_total_indirect_diffuse = 0.; + for (int x = 0; x <= ATROUS_KERNEL_WIDTH; ++x) { + for (int y = 0; y <= ATROUS_KERNEL_WIDTH; ++y) { + const ivec2 offset = ivec2(x, y); + + // 3. Indirect diffuse + { + const float sn_phi = .5; + const float p_phi = 3.; + const int step_width = 1; + ivec2 p; + const float weight = aTrousSampleWeigth( + res, pix, pos, shading_normal, offset, step_width, INDIRECT_SCALE, sn_phi, p_phi, p); + + if (weight > 0.) { + const bool do_indirect = all(lessThan(p, res_scaled)); + if (do_indirect) { + weight_total_indirect_diffuse += weight; + indiff += imageLoad(indirect_diffuse, p).rgb * weight; + } + } + } + } // for y + } // for x + + const float one_over_weight_indirect_diffuse = weight_total_indirect_diffuse == 0. ? 0 : 1. / weight_total_indirect_diffuse; + indiff *= one_over_weight_indirect_diffuse; + + //indiff = imageLoad(indirect_diffuse, pix).rgb; + + imageStore(out_indirect_diffuse_atrous1, pix, vec4(indiff, 0./*unused*/)); +} diff --git a/ref/vk/shaders/rt.json b/ref/vk/shaders/rt.json index f66eb0cde2..2197957cef 100644 --- a/ref/vk/shaders/rt.json +++ b/ref/vk/shaders/rt.json @@ -38,6 +38,9 @@ "bounce": { "comp": "bounce" }, + "indiff_at1": { + "comp": "indirect_diffuse_atrous1" + }, "denoiser": { "comp": "denoiser" },