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

392 lines
14 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 "debug.glsl"
#include "utils.glsl"
#include "color_spaces.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;
// Blatantly copypasted from https://www.shadertoy.com/view/XsGfWV
vec3 aces_tonemap(vec3 color){
mat3 m1 = mat3(
0.59719, 0.07600, 0.02840,
0.35458, 0.90834, 0.13383,
0.04823, 0.01566, 0.83777
);
mat3 m2 = mat3(
1.60475, -0.10208, -0.00327,
-0.53108, 1.10813, -0.07276,
-0.07367, -0.00605, 1.07602
);
vec3 v = m1 * color;
vec3 a = v * (v + 0.0245786) - 0.000090537;
vec3 b = v * (0.983729 * v + 0.4329510) + 0.238081;
//return pow(clamp(m2 * (a / b), 0.0, 1.0), vec3(1.0 / 2.2));
return clamp(m2 * (a / b), 0.0, 1.0);
}
vec3 reinhard(vec3 color){
return color / (color + 1.0);
}
vec3 reinhard02(vec3 c, vec3 Cwhite2) {
return c * (1. + c / Cwhite2) / (1. + c);
}
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;
VALIDATE_VEC3(c.direct_specular, "%d c.direct_specular=(%f,%f,%f) poly");
c.direct_specular += imageLoad(light_point_specular, p).rgb * specular_scale;
VALIDATE_VEC3(c.direct_specular, "%d c.direct_specular=(%f,%f,%f) point");
}
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 (IS_INVALID3(c.direct_specular)) {
#ifdef SHADER_DEBUG_ENABLE
debugPrintfEXT("c.direct_specular=(%f,%f,%f)", PRIVEC3(c.direct_specular));
#endif
c.direct_specular = vec3(0.);
}
if (IS_INVALID(direct_specular_total)) {
#ifdef SHADER_DEBUG_ENABLE
debugPrintfEXT("direct_specular_total=%f", direct_specular_total);
#endif
direct_specular_total = 0.;
}
if (direct_specular_total > 0.)
c.direct_specular *= direct_specular_total;
if (indirect_specular_total > 0.)
c.indirect_specular *= indirect_specular_total;
if (IS_INVALID3(c.indirect_specular)) {
#ifdef SHADER_DEBUG_ENABLE
debugPrintfEXT("c.indirect_specular=(%f,%f,%f)", PRIVEC3(c.indirect_specular));
#endif
c.indirect_specular = vec3(0.);
}
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_DIRECT_DIFF) {
imageStore(out_dest, pix, vec4(LINEARtoSRGB(c.direct_diffuse), 0.)); return;
return;
} else if (ubo.ubo.debug_display_only == DEBUG_DISPLAY_DIRECT_SPEC) {
imageStore(out_dest, pix, vec4(LINEARtoSRGB(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 (IS_INVALID3(history_specular)) {
#ifdef SHADER_DEBUG_ENABLE
debugPrintfEXT("PRE pix=(%d,%d) history_specular=inv", pix.x, pix.y);
#endif
history_specular = vec3(0.);
}
if (IS_INVALID3(specular)) {
#ifdef SHADER_DEBUG_ENABLE
debugPrintfEXT("PRE pix=(%d,%d) specular=(%f,%f,%f)", pix.x, pix.y, PRIVEC3(specular));
#endif
specular = vec3(0.);
}
vec3 new_specular = vec3(0.);
if (better_depth_offset < depth_treshold) {
diffuse = mix(diffuse, history_diffuse, 0.8);
new_specular = mix(specular, history_specular, 0.3);
}
if (IS_INVALID3(diffuse)) {
#ifdef SHADER_DEBUG_ENABLE
debugPrintfEXT("pix=(%d,%d) diffuse=inv", pix.x, pix.y);
#endif
diffuse = vec3(0.);
}
if (IS_INVALID3(new_specular)) {
#ifdef SHADER_DEBUG_ENABLE
debugPrintfEXT("pix=(%d,%d) new_specular=inv, specular=(%f, %f, %f) history_specular=(%f, %f, %f)",
pix.x, pix.y,
specular.r, specular.g, specular.b,
history_specular.r, history_specular.g, history_specular.b
);
#endif
new_specular = vec3(0.);
}
specular = new_specular;
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);
const float metalness = imageLoad(material_rmxx, pix).g;
colour = mixFinalColor(base_color, diffuse, specular, metalness);
} 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;
colour = LINEARtoSRGB(colour);
// 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.
colour += legacy_blend.rgb;
imageStore(out_dest, pix, vec4(colour, 0./*unused*/));
}