xash3d-fwgs/ref/vk/shaders/denoiser.comp

323 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#version 460
#include "utils.glsl"
#include "color_spaces.glsl"
#include "tonemapping.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, rgba16f) uniform image2D out_dest;
layout(set = 0, binding = 1, rgba8) uniform readonly image2D base_color_a;
layout(set = 0, binding = 2, rgba16f) uniform readonly image2D light_poly_diffuse;
layout(set = 0, binding = 3, rgba16f) uniform readonly image2D light_poly_specular;
layout(set = 0, binding = 4, rgba16f) uniform readonly image2D light_point_diffuse;
layout(set = 0, binding = 5, rgba16f) uniform readonly image2D light_point_specular;
layout(set = 0, binding = 6, rgba16f) uniform readonly image2D emissive;
layout(set = 0, binding = 7, rgba32f) uniform readonly image2D position_t;
layout(set = 0, binding = 8, rgba16f) uniform readonly image2D normals_gs;
layout(set = 0, binding = 9, rgba8) uniform readonly image2D material_rmxx;
layout(set = 0, binding = 10, rgba32f) uniform readonly image2D geometry_prev_position;
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 = 14, rgba16f) uniform image2D out_temporal_diffuse;
layout(set = 0, binding = 15, 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;
//#define DEBUG_NOISE
#ifdef DEBUG_NOISE
layout(set = 0, binding = 18) 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) uniform sampler2D textures[MAX_TEXTURES];
const int INDIRECT_SCALE = 2;
float normpdf2(in float x2, in float sigma) { return 0.39894*exp(-0.5*x2/(sigma*sigma))/sigma; }
float normpdf(in float x, in float sigma) { return normpdf2(x*x, sigma); }
void readNormals(ivec2 uv, out vec3 geometry_normal, out vec3 shading_normal) {
const vec4 n = imageLoad(normals_gs, uv);
geometry_normal = normalDecode(n.xy);
shading_normal = normalDecode(n.zw);
}
struct Components {
vec3 direct_diffuse, direct_specular, indirect_diffuse, indirect_specular;
};
Components blurSamples(const ivec2 res, const ivec2 pix) {
Components c;
c.direct_diffuse = c.direct_specular = c.indirect_diffuse = c.indirect_specular = vec3(0.);
const vec4 center_pos = imageLoad(position_t, pix);
const int DIRECT_DIFFUSE_KERNEL = 3;
const int INDIRECT_DIFFUSE_KERNEL = 5;
const int DIRECT_SPECULAR_KERNEL = 2;
const int INDIRECT_SPECULAR_KERNEL = 2;
const int KERNEL_SIZE = max(max(max(DIRECT_DIFFUSE_KERNEL, INDIRECT_DIFFUSE_KERNEL), DIRECT_SPECULAR_KERNEL), INDIRECT_SPECULAR_KERNEL);
const float direct_diffuse_sigma = DIRECT_DIFFUSE_KERNEL / 2.;
const float indirect_diffuse_sigma = INDIRECT_DIFFUSE_KERNEL / 2.;
const float direct_specular_sigma = DIRECT_SPECULAR_KERNEL / 2.;
const float indirect_specular_sigma = INDIRECT_SPECULAR_KERNEL / 2.;
float direct_diffuse_total = 0.;
float indirect_diffuse_total = 0.;
float direct_specular_total = 0.;
float indirect_specular_total = 0.;
const ivec2 res_scaled = res / INDIRECT_SCALE;
for (int x = -KERNEL_SIZE; x <= KERNEL_SIZE; ++x)
for (int y = -KERNEL_SIZE; y <= KERNEL_SIZE; ++y) {
const ivec2 p = pix + ivec2(x, y);
if (any(greaterThanEqual(p, res)) || any(lessThan(p, ivec2(0)))) {
continue;
}
vec3 sample_geometry_normal, sample_shading_normal;
readNormals(p, sample_geometry_normal, sample_shading_normal);
float scale = 1.f;
// FIXME also filter by depth, (kusok index?), etc
//scale *= smoothstep(.9, 1., dot(sample_geometry_normal, geometry_normal));
const vec4 sample_pos = imageLoad(position_t, p);
// FIXME what are these magic numbers?
scale *= smoothstep(center_pos.w * 4. / 100., 0., distance(center_pos.xyz, sample_pos.xyz));
if ( scale <= 0. )
continue;
const ivec2 p_indirect = pix / INDIRECT_SCALE + ivec2(x, y);
const bool do_indirect = all(lessThan(p_indirect, res_scaled)) && all(greaterThanEqual(p_indirect, ivec2(0)));
if (all(lessThan(abs(ivec2(x, y)), ivec2(DIRECT_DIFFUSE_KERNEL))))
{
const float direct_diffuse_scale = scale * normpdf(x, direct_diffuse_sigma) * normpdf(y, direct_diffuse_sigma);
direct_diffuse_total += direct_diffuse_scale;
c.direct_diffuse += imageLoad(light_point_diffuse, p).rgb * direct_diffuse_scale;
c.direct_diffuse += imageLoad(light_poly_diffuse, p).rgb * direct_diffuse_scale;
}
if (all(lessThan(abs(ivec2(x, y)), ivec2(INDIRECT_DIFFUSE_KERNEL))) && do_indirect)
{
// TODO indirect operates at different scale, do a separate pass
const float indirect_diffuse_scale = scale
* normpdf(x, indirect_diffuse_sigma)
* normpdf(y, indirect_diffuse_sigma);
indirect_diffuse_total += indirect_diffuse_scale;
c.indirect_diffuse += imageLoad(indirect_diffuse, p_indirect).rgb * indirect_diffuse_scale;
}
if (all(lessThan(abs(ivec2(x, y)), ivec2(DIRECT_SPECULAR_KERNEL))))
{
const float specular_scale = scale * normpdf(x, direct_specular_sigma) * normpdf(y, direct_specular_sigma);
direct_specular_total += specular_scale;
c.direct_specular += imageLoad(light_poly_specular, p).rgb * specular_scale;
c.direct_specular += imageLoad(light_point_specular, p).rgb * specular_scale;
}
if (all(lessThan(abs(ivec2(x, y)), ivec2(INDIRECT_SPECULAR_KERNEL)))) {
const ivec2 p_indirect = (pix + ivec2(x, y)) / INDIRECT_SCALE;// + ivec2(x, y);
const bool do_indirect = all(lessThan(p_indirect, res_scaled)) && all(greaterThanEqual(p_indirect, ivec2(0)));
if (do_indirect) {
// TODO indirect operates at different scale, do a separate pass
const float specular_scale = scale * normpdf(x, indirect_specular_sigma) * normpdf(y, indirect_specular_sigma);
indirect_specular_total += specular_scale;
c.indirect_specular += imageLoad(indirect_specular, p_indirect).rgb * specular_scale;
}
}
}
if (direct_diffuse_total > 0.)
c.direct_diffuse /= direct_diffuse_total;
if (indirect_diffuse_total > 0.)
c.indirect_diffuse *= indirect_diffuse_total;
if (direct_specular_total > 0.)
c.direct_specular *= direct_specular_total;
if (indirect_specular_total > 0.)
c.indirect_specular *= indirect_specular_total;
return c;
}
void main() {
const ivec2 res = ubo.ubo.res;
const ivec2 pix = ivec2(gl_GlobalInvocationID);
if (any(greaterThanEqual(pix, res))) {
return;
}
if (ubo.ubo.debug_display_only == DEBUG_DISPLAY_DISABLED) {
// no-op, just continue
} else if (ubo.ubo.debug_display_only == DEBUG_DISPLAY_BASECOLOR) {
imageStore(out_dest, pix, vec4(LINEARtoSRGB(imageLoad(base_color_a, pix).rgb), 0.)); return;
return;
} else if (ubo.ubo.debug_display_only == DEBUG_DISPLAY_BASEALPHA) {
imageStore(out_dest, pix, imageLoad(base_color_a, pix).aaaa); return;
return;
} else if (ubo.ubo.debug_display_only == DEBUG_DISPLAY_EMISSIVE) {
imageStore(out_dest, pix, vec4(LINEARtoSRGB(imageLoad(emissive, pix).rgb), 0.)); return;
return;
} else if (ubo.ubo.debug_display_only == DEBUG_DISPLAY_NSHADE) {
vec3 geometry_normal, shading_normal;
readNormals(pix, geometry_normal, shading_normal);
imageStore(out_dest, pix, vec4(.5 + shading_normal * .5, 0.));
return;
} else if (ubo.ubo.debug_display_only == DEBUG_DISPLAY_NGEOM) {
vec3 geometry_normal, shading_normal;
readNormals(pix, geometry_normal, shading_normal);
imageStore(out_dest, pix, vec4(.5 + geometry_normal * .5, 0.));
return;
}
#ifdef DEBUG_NOISE
imageStore(out_dest, pix, blueNoise(ivec3(pix.xy, ubo.ubo.frame_counter))); return;
#endif
const Components c = blurSamples(res, pix);
if (ubo.ubo.debug_display_only == DEBUG_DISPLAY_DISABLED) {
} else if (ubo.ubo.debug_display_only == DEBUG_DISPLAY_DIRECT) {
imageStore(out_dest, pix, vec4(LINEARtoSRGB(c.direct_diffuse + c.direct_specular), 0.)); return;
return;
} else if (ubo.ubo.debug_display_only == DEBUG_DISPLAY_INDIRECT) {
imageStore(out_dest, pix, vec4(LINEARtoSRGB(c.indirect_diffuse + c.indirect_specular), 0.)); return;
return;
} else if (ubo.ubo.debug_display_only == DEBUG_DISPLAY_INDIRECT_SPEC) {
imageStore(out_dest, pix, vec4(LINEARtoSRGB(c.indirect_specular), 0.)); return;
return;
} else if (ubo.ubo.debug_display_only == DEBUG_DISPLAY_INDIRECT_DIFF) {
imageStore(out_dest, pix, vec4(LINEARtoSRGB(c.indirect_diffuse), 0.)); return;
return;
}
vec3 diffuse = c.direct_diffuse + c.indirect_diffuse;
vec3 specular = c.direct_specular + c.indirect_specular;
{
//#define DISABLE_TEMPORAL_DENOISER
#ifndef DISABLE_TEMPORAL_DENOISER
// TODO: need to extract reprojecting from this shader because reprojected stuff need svgf denoising pass after it
const vec3 origin = (ubo.ubo.inv_view * vec4(0., 0., 0., 1.)).xyz;
const vec3 position = imageLoad(position_t, pix).xyz;
const float depth = length(origin - position);
const vec3 prev_position = imageLoad(geometry_prev_position, pix).rgb;
const vec4 clip_space = inverse(ubo.ubo.prev_inv_proj) * vec4((inverse(ubo.ubo.prev_inv_view) * vec4(prev_position, 1.)).xyz, 1.);
const vec2 reproj_uv = clip_space.xy / clip_space.w;
const ivec2 reproj_pix = ivec2((reproj_uv * 0.5 + vec2(0.5)) * vec2(res));
const vec3 prev_origin = (ubo.ubo.prev_inv_view * vec4(0., 0., 0., 1.)).xyz;
const float depth_nessesary = length(prev_position - prev_origin);
const float depth_treshold = 0.01 * clip_space.w;
float better_depth_offset = depth_treshold;
vec3 history_diffuse = diffuse;
vec3 history_specular = specular;
const int TEMPORAL_KERNEL = 1; // lifekilled says it should be fixed
for(int x = -TEMPORAL_KERNEL; x <=TEMPORAL_KERNEL; x++) {
for(int y = -TEMPORAL_KERNEL; y <=TEMPORAL_KERNEL; y++) {
const ivec2 p = reproj_pix + ivec2(x, y);
if (any(greaterThanEqual(p, res)) || any(lessThan(p, ivec2(0)))) {
continue;
}
if (any(greaterThanEqual(reproj_pix, res)) || any(lessThan(reproj_pix, ivec2(0)))) {
continue;
}
const vec4 history_diffuse_depth = imageLoad( prev_temporal_diffuse, reproj_pix );
const vec4 history_specular_sample = imageLoad( prev_temporal_specular, reproj_pix );
const float history_depth = history_diffuse_depth.w;
const float depth_offset = abs(history_depth - depth_nessesary);
if ( depth_offset < better_depth_offset ) {
better_depth_offset = depth_offset;
history_diffuse = history_diffuse_depth.rgb;
history_specular = history_specular_sample.rgb;
}
}
}
if (better_depth_offset < depth_treshold) {
diffuse = mix(diffuse, history_diffuse, 0.8);
specular = mix(specular, history_specular, 0.3);
}
imageStore(out_temporal_diffuse, pix, vec4(diffuse, depth));
imageStore(out_temporal_specular, pix, vec4(specular, 0./*unused*/));
#endif // ifndef DISABLE_TEMPORAL_DENOISER
}
vec3 colour = vec3(0.);
if (ubo.ubo.debug_display_only != DEBUG_DISPLAY_LIGHTING) {
const vec3 base_color = SRGBtoLINEAR(imageLoad(base_color_a, pix).rgb);
colour = diffuse * base_color;
colour += specular * mix(vec3(1.), base_color, imageLoad(material_rmxx, pix).g);
} else {
colour = diffuse + specular;
}
const vec4 legacy_blend = imageLoad(legacy_blend, pix);
colour += imageLoad(emissive, pix).rgb;
// Revealage. TODO: which colorspace?
colour *= legacy_blend.a;
// See issue https://github.com/w23/xash3d-fwgs/issues/668, map test_blendmode_additive_alpha.
// Adding emissive_blend to the final color in the *incorrect* sRGB-γ space. It makes
// it look much more like the original. Adding emissive in the *correct* linear space differs
// from the original a lot, and looks perceptively worse.
switch (ubo.ubo.vk_display_dr_mode) {
// LDR: VK_FORMAT_B8G8R8A8_UNORM(44) VK_COLOR_SPACE_SRGB_NONLINEAR_KHR(0)
case LDR_B8G8R8A8_UNORM_SRGB_NONLINEAR:
colour = LINEARtoSRGB(colour);
colour += legacy_blend.rgb;
break;
// LDR: VK_FORMAT_R8G8B8A8_SRGB(43) VK_COLOR_SPACE_SRGB_NONLINEAR_KHR(0)
// LDR: VK_FORMAT_R8G8B8A8_UNORM(37) VK_COLOR_SPACE_SRGB_NONLINEAR_KHR(0)
// LDR: VK_FORMAT_B8G8R8A8_SRGB(50) VK_COLOR_SPACE_SRGB_NONLINEAR_KHR(0)
// LDR: VK_FORMAT_A2B10G10R10_UNORM_PACK32(64) VK_COLOR_SPACE_SRGB_NONLINEAR_KHR(0)
// HDR: VK_FORMAT_R16G16B16A16_SFLOAT(97) VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT(1000104002)
case HDR_R16G16B16A16_SFLOAT_EXTENDED_SRGB_LINEAR:
colour += SRGBtoLINEAR(legacy_blend.rgb*5); // hack
break;
// HDR: VK_FORMAT_A2B10G10R10_UNORM_PACK32(64) VK_COLOR_SPACE_HDR10_ST2084_EXT(1000104008)
case HDR_A2B10G10R10_UNORM_PACK32_HDR10_ST2084:
colour = linearToPQ(applyHuePreservingShoulder(colour), 100);
colour += agx(SRGBtoLINEAR(legacy_blend.rgb)*0.5); // hack
break;
}
imageStore(out_dest, pix, vec4(colour, 0./*unused*/));
}