Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Denoise diffuse indirect lighting #750

Merged
merged 7 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions ref/vk/shaders/bounce.comp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ layout(set = 0, binding = 13, rgba8) uniform readonly image2D base_color_a;

layout(set = 0, binding = 20, rgba16f) uniform writeonly image2D out_indirect_diffuse;
layout(set = 0, binding = 21, rgba16f) uniform writeonly image2D out_indirect_specular;
layout(set = 0, binding = 22, rgba16f) uniform writeonly image2D out_first_bounce_direction; // for spherical harmonics denoising

layout(set = 0, binding = 30, std430) readonly buffer ModelHeaders { ModelHeader a[]; } model_headers;
layout(set = 0, binding = 31, std430) readonly buffer Kusochki { Kusok a[]; } kusochki;
Expand Down Expand Up @@ -94,6 +95,8 @@ bool getHit(vec3 origin, vec3 direction, inout RayPayloadPrimary payload) {
payload.hit_t.w = L;
payload.emissive.rgb = sampleSkybox(direction);
return false;
} else {
payload.emissive.rgb = vec3(0.); // emissive polygons already calculated in directional polygon lighting pass (remove fireflyes here)
}

primaryRayHit(rq, payload);
Expand Down Expand Up @@ -219,6 +222,9 @@ void computeBounces(MaterialEx mat, vec3 pos, vec3 direction, inout vec3 diffuse
else
specular += contribution;

if (i == 0)
imageStore(out_first_bounce_direction, pix, vec4(normalize(bounce_direction), 0.f)); // for spherical harmonics denoising

if (!did_hit)
break;

Expand All @@ -243,6 +249,12 @@ void main() {
return;
}
const vec2 uv = (gl_GlobalInvocationID.xy + .5) / res * 2. - 1.;

if ((ubo.ubo.renderer_flags & RENDERER_FLAG_DISABLE_GI) != 0) {
imageStore(out_indirect_diffuse, pix, vec4(0.));
imageStore(out_indirect_specular, pix, vec4(0.));
return;
}

#ifdef BRDF_COMPARE
g_mat_gltf2 = pix.x > ubo.ubo.res.x / INDIRECT_SCALE / 2.;
Expand All @@ -265,11 +277,17 @@ void main() {

MaterialEx mat;
mat.prop.base_color = base_a.rgb;
mat.prop.metalness = material_data.g;
mat.prop.roughness = material_data.r;
mat.geometry_normal = geometry_normal;
mat.shading_normal = shading_normal;

if ((ubo.ubo.renderer_flags & RENDERER_FLAG_ONLY_DIFFUSE_GI) != 0) {
mat.prop.metalness = 0.0;
mat.prop.roughness = 1.0;
} else {
mat.prop.metalness = material_data.g;
mat.prop.roughness = material_data.r;
}

computeBounces(mat, pos_t.xyz, direction, diffuse, specular);
}

Expand All @@ -288,6 +306,11 @@ void main() {
DEBUG_VALIDATE_RANGE_VEC3("bounce.specular", specular, 0., 1e6);
#endif

if ((ubo.ubo.renderer_flags & RENDERER_FLAG_ONLY_DIFFUSE_GI) != 0) {
diffuse += specular;
specular = vec3(0.);
}

imageStore(out_indirect_diffuse, pix, vec4(diffuse, 0.f));
imageStore(out_indirect_specular, pix, vec4(specular, 0.f));
}
31 changes: 23 additions & 8 deletions ref/vk/shaders/denoiser.comp
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,23 @@ 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_diffuse_atrous1;
layout(set = 0, binding = 14, rgba16f) uniform readonly image2D indirect_specular;
layout(set = 0, binding = 15, rgba16f) uniform readonly image2D indirect_diffuse_denoised_by_sh;

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_diffuse;
layout(set = 0, binding = 17, rgba16f) uniform image2D prev_temporal_diffuse;

layout(set = 0, binding = 17, rgba16f) uniform image2D out_temporal_specular;
layout(set = 0, binding = 18, rgba16f) uniform image2D prev_temporal_specular;
layout(set = 0, binding = 18, rgba16f) uniform image2D out_temporal_specular;
layout(set = 0, binding = 19, rgba16f) uniform image2D prev_temporal_specular;

//#define DEBUG_NOISE
#ifdef DEBUG_NOISE
layout(set = 0, binding = 19) uniform sampler3D blue_noise_texture;
layout(set = 0, binding = 20) uniform sampler3D blue_noise_texture;
#include "bluenoise.glsl"
#endif

layout(set = 0, binding = 20, rgba16f) uniform readonly image2D legacy_blend;
layout(set = 0, binding = 21, rgba16f) uniform readonly image2D legacy_blend;

//layout(set = 0, binding = 21) uniform sampler2D textures[MAX_TEXTURES];
//layout(set = 0, binding = 22) uniform sampler2D textures[MAX_TEXTURES];

#include "atrous.glsl"

Expand Down Expand Up @@ -98,7 +99,11 @@ Components dontBlurSamples(const ivec2 res, const ivec2 pix) {
const ivec2 p_indirect = pix / INDIRECT_SCALE;
c.direct_diffuse += imageLoad(light_point_diffuse, p).rgb;
c.direct_diffuse += imageLoad(light_poly_diffuse, p).rgb;
if ((ubo.ubo.renderer_flags & RENDERER_FLAG_DENOISE_GI_BY_SH) == 0) {
c.indirect_diffuse += imageLoad(indirect_diffuse, p_indirect).rgb;
} else {
c.indirect_diffuse += imageLoad(indirect_diffuse_denoised_by_sh, p).rgb;
}
c.direct_specular += imageLoad(light_poly_specular, p).rgb;
c.direct_specular += imageLoad(light_point_specular, p).rgb;
c.indirect_specular += imageLoad(indirect_specular, p_indirect).rgb;
Expand Down Expand Up @@ -142,7 +147,11 @@ Components boxBlurSamples(ivec2 res, ivec2 pix) {

res /= 2;
pix /= 2;

if ((ubo.ubo.renderer_flags & RENDERER_FLAG_DENOISE_GI_BY_SH) == 0) {
BOX_BLUR(c.indirect_diffuse, indirect_diffuse, res, pix, INDIRECT_DIFFUSE_KERNEL);
}

BOX_BLUR(c.indirect_specular, indirect_specular, res, pix, INDIRECT_SPECULAR_KERNEL);

return c;
Expand Down Expand Up @@ -309,7 +318,8 @@ Components blurSamples(const ivec2 res, const ivec2 pix) {
c.direct_diffuse += imageLoad(light_poly_diffuse, p).rgb * direct_diffuse_scale;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это нормально, что это изменение не под флагом?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это ошибка, я удалил лишнюю строку, исправлю

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Исправил

}

if (all(lessThan(abs(ivec2(x, y)), ivec2(INDIRECT_DIFFUSE_KERNEL))) && do_indirect)
if ((ubo.ubo.renderer_flags & RENDERER_FLAG_DENOISE_GI_BY_SH) == 0 &&
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
Expand Down Expand Up @@ -452,6 +462,11 @@ void main() {

vec3 diffuse = c.direct_diffuse + c.indirect_diffuse;
vec3 specular = c.direct_specular + c.indirect_specular;

if ((ubo.ubo.renderer_flags & RENDERER_FLAG_DENOISE_GI_BY_SH) != 0) {
diffuse += imageLoad(indirect_diffuse_denoised_by_sh, pix).rgb;
}

{
//#define DISABLE_TEMPORAL_DENOISER
#ifndef DISABLE_TEMPORAL_DENOISER
Expand Down
11 changes: 11 additions & 0 deletions ref/vk/shaders/denoiser_config.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

// not plane, it's sphere, but working
#define NEAR_PLANE_OFFSET 5.

// we downsample gi map and store bounces positions in neighboor texels
// downsample image dimensions by 2 = store 4 bounces
// downsample image dimensions by 3 = store 9 bounces
#define GI_DOWNSAMPLE 2

// max bounces for testing bounces visiblity
#define GI_BOUNCES_MAX 1
197 changes: 197 additions & 0 deletions ref/vk/shaders/denoiser_utils.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#ifndef lk_dnsr_utils_LK_12231312
#define lk_dnsr_utils_LK_12231312 1

#define TEXEL_FLAG_TRANSPARENT 1
#define TEXEL_FLAG_REFRACTION 2

// clamp light exposition without loosing of color
vec3 clamp_color(vec3 color, float clamp_value) {
float max_color = max(max(color.r, color.g), color.b);
return max_color > clamp_value ? (color / max_color) * clamp_value : color;
}

// 3-th component is transparent texel status 0 or 1
ivec3 PixToCheckerboard(ivec2 pix, ivec2 res) {
int is_transparent_texel = (pix.x + pix.y) % 2;
ivec2 out_pix = ivec2(pix.x / 2 + is_transparent_texel * (res.x / 2), pix.y);
return ivec3(out_pix, is_transparent_texel);
}

// 3-th component is transparent texel status 0 or 1, targeted to nesessary texel status
ivec3 PixToCheckerboard(ivec2 pix, ivec2 res, int is_transparent_texel) {
ivec2 out_pix = ivec2(pix.x / 2 + is_transparent_texel * (res.x / 2), pix.y);
return ivec3(out_pix, is_transparent_texel);
}

// optional choose checkerboard conversion if there is real transparence or not
ivec3 PixToCheckerboard(ivec2 pix, ivec2 res, int is_transparent_texel, int texel_flags) {
if (texel_flags == TEXEL_FLAG_TRANSPARENT || texel_flags == TEXEL_FLAG_REFRACTION) {
return PixToCheckerboard(pix, res, is_transparent_texel);
}
return PixToCheckerboard(pix, res);
}


// 3-th component is transparent texel status 0 or 1
ivec3 CheckerboardToPix(ivec2 pix, ivec2 res) {
int half_res = res.x / 2;
int is_transparent_texel = pix.x / half_res;
int out_pix_x = (pix.x % half_res) * 2;
int row_index = pix.y % 2;
int checker_addition = is_transparent_texel + row_index - row_index*is_transparent_texel*2;
ivec2 out_pix = ivec2(out_pix_x + checker_addition, pix.y);
return ivec3(out_pix, is_transparent_texel);
}


vec3 OriginWorldPosition(mat4 inv_view) {
return (inv_view * vec4(0, 0, 0, 1)).xyz;
}

vec3 ScreenToWorldDirection(vec2 uv, mat4 inv_view, mat4 inv_proj) {
vec4 target = inv_proj * vec4(uv.x, uv.y, 1, 1);
vec3 direction = (inv_view * vec4(normalize(target.xyz), 0)).xyz;
return normalize(direction);
}

vec3 WorldPositionFromDirection(vec3 origin, vec3 direction, float depth) {
return origin + normalize(direction) * depth;
}

vec3 FarPlaneDirectedVector(vec2 uv, vec3 forward, mat4 inv_view, mat4 inv_proj) {
vec3 dir = ScreenToWorldDirection(uv, inv_view, inv_proj);
float plane_length = dot(forward, dir);
return dir / max(0.001, plane_length);
}

vec2 WorldPositionToUV(vec3 position, mat4 proj, mat4 view) {
vec4 clip_space = proj * vec4((view * vec4(position, 1.)).xyz, 1.);
return clip_space.xy / clip_space.w;
}

vec3 WorldPositionToUV2(vec3 position, mat4 inv_proj, mat4 inv_view) {
const vec3 out_of_bounds = vec3(0.,0.,-1.);
const float near_plane_treshold = 1.;
vec3 origin = OriginWorldPosition(inv_view);
vec3 forwardDirection = normalize(ScreenToWorldDirection(vec2(0.), inv_view, inv_proj));
float depth = dot(forwardDirection, position - origin);
if (depth < near_plane_treshold) return out_of_bounds;
vec3 positionNearPlane = (position - origin) / depth;
vec3 rightForwardDirection = ScreenToWorldDirection(vec2(1., 0.), inv_view, inv_proj);
vec3 upForwardDirection = ScreenToWorldDirection(vec2(0., 1.), inv_view, inv_proj);
rightForwardDirection /= dot(forwardDirection, rightForwardDirection);
upForwardDirection /= dot(forwardDirection, upForwardDirection);
vec3 rightDirection = rightForwardDirection - forwardDirection;
vec3 upDirection = upForwardDirection - forwardDirection;
float x = dot(normalize(rightDirection), positionNearPlane - forwardDirection) / length(rightDirection);
float y = dot(normalize(upDirection), positionNearPlane - forwardDirection) / length(upDirection);
if (x < -1. || y < -1. || x > 1. || y > 1.) return out_of_bounds;
return vec3(x, y, 1.);
}

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

ivec2 UVToPix(vec2 uv, ivec2 res) {
vec2 screen_uv = uv * 0.5 + vec2(0.5);
return ivec2(screen_uv.x * float(res.x), screen_uv.y * float(res.y));
}

vec2 PixToUV(ivec2 pix, ivec2 res) {
return (vec2(pix) /*+ vec2(0.5)*/) / vec2(res) * 2. - vec2(1.);
}

vec3 PBRMix(vec3 base_color_a, vec3 diffuse, vec3 specular, float metalness) {
vec3 metal_colour = specular * base_color_a;
vec3 dielectric_colour = mix(diffuse * base_color_a, specular, 0.04); // like in Unreal
return mix(dielectric_colour, metal_colour, metalness);
}

vec3 PBRMixFresnel(vec3 base_color_a, vec3 diffuse, vec3 specular, float metalness, float fresnel) {
vec3 metal_colour = specular * base_color_a;
float diffuse_specular_factor = mix(0.2, 0.04, fresnel);
vec3 dielectric_colour = mix(diffuse * base_color_a, specular, diffuse_specular_factor);
return mix(dielectric_colour, metal_colour, metalness);
}

int per_frame_offset = 0;

int quarterPart(ivec2 pix_in) {
ivec2 pix = pix_in % 2;
return (pix.x + 2 * pix.y + per_frame_offset) % 4;
}

int ninefoldPart(ivec2 pix_in) {
ivec2 pix = pix_in % 3;
return (pix.x + 3 * pix.y + per_frame_offset) % 9;
}

int texel_transparent_type(float transparent_alpha) {
return abs(transparent_alpha) < 0.05 ? 0 : transparent_alpha > 0. ? 2 : 3;
}

int checker_texel(ivec2 pix) {
return (pix.x + pix.y) % 2;
}

ivec2 closest_checker_texel(ivec2 pix, int source_checker_texel) {
return checker_texel(pix) == source_checker_texel ? pix : pix + ivec2(1, 0);
}


#ifndef M_PI
#define M_PI 3.1488
#endif

// Schlick's approximation to Fresnel term
// f90 should be 1.0, except for the trick used by Schuler (see 'shadowedF90' function)
vec3 evalFresnelSchlickM(vec3 f0, float f90, float NdotS) {
return f0 + (f90 - f0) * pow(1.0f - NdotS, 5.0f);
}

float luminanceM(vec3 rgb) {
return dot(rgb, vec3(0.2126f, 0.7152f, 0.0722f));
}

vec3 randomizedOnHemisphere(vec3 randomVec, vec3 normal) {
float directionality = dot(normal, randomVec);
if (directionality > 0.) return normalize(randomVec);
return -normalize(randomVec);
}

vec3 sampleSphere(vec2 uv)
{
float y = 2.0 * uv.x - 1;
float theta = 2.0 * M_PI * uv.y;
float r = sqrt(1.0 - y * y);
return vec3(cos(theta) * r, y, sin(theta) * r);
}

// Microfacet bounce from this example https://www.shadertoy.com/view/Md3yWl

vec3 SphereRand( vec2 rand )
{
rand += vec2(.5);
float sina = rand.x*2. - 1.;
float b = 6.283*rand.y;
float cosa = sqrt(1.-sina*sina);
return vec3(cosa*cos(b),sina,cosa*sin(b));
}

vec3 PowRand( vec3 rand, vec3 axis, float fpow )
{
//vec3 r = normalize(rand - vec3(0.5));
vec3 r = sampleSphere(rand.xz);
//vec3 r = SphereRand(rand.xy);
float d = dot(r,axis);
r -= d*axis;
r = normalize(r);
float h = d*.5+.5;
r *= sqrt( 1. - pow( h, 2./(fpow+1.) ) );
r += axis*sqrt(1.-dot(r,r));
return r;
}

#define FIX_NAN(COLOR) (any(isnan(COLOR)) ? vec4(0.) : COLOR)

#endif // #ifndef lk_dnsr_utils_LK_12231312
Loading