Files
bgfx/examples/xx-denoise/normal_encoding.sh
elvencache a33ca71bcf add denoise example (#2344)
/*
* Implement SVGF style denoising as bgfx example. Goal is to explore various
* options and parameters, not produce an optimized, efficient denoiser.
*
* Starts with deferred rendering scene with very basic lighting. Lighting is
* masked out with a noise pattern to provide something to denoise. There are
* two options for the noise pattern. One is a fixed 2x2 dither pattern to
* stand-in for lighting at quarter resolution. The other is the common
* shadertoy random pattern as a stand-in for some fancier lighting without
* enough samples per pixel, like ray tracing.
*
* First a temporal denoising filter is applied. The temporal filter is only
* using normals to reject previous samples. The SVGF paper also describes using
* depth comparison to reject samples but that is not implemented here.
*
* Followed by some number of spatial filters. These are implemented like in the
* SVGF paper. As an alternative to the 5x5 Edge-Avoiding A-Trous filter, can
* select a 3x3 filter instead. The 3x3 filter takes fewer samples and covers a
* smaller area, but takes less time to compute. From a loosely eyeballed
* comparison, N 5x5 passes looks similar to N+1 3x3 passes. The wider spatial
* filters take a fair chunk of time to compute. I wonder if it would be a good
* idea to interleave the input texture before computing, after the first pass
* which skips zero pixels.
*
* I have not implemetened the variance guided part.
*
* There's also an optional TXAA pass to be applied after. I am not happy with
* its implementation yet, so it defaults to off here.
*/

/*
* References:
* Spatiotemporal Variance-Guided Filtering: Real-Time Reconstruction for
*	Path-Traced Global Illumination. by Christoph Schied and more.
*	- SVGF denoising algorithm
*
* Streaming G-Buffer Compression for Multi-Sample Anti-Aliasing.
*	by E. Kerzner and M. Salvi.
*	- details about history comparison for temporal denoising filter
*
* Edge-Avoiding À-Trous Wavelet Transform for Fast Global Illumination
*	Filtering. by Holger Dammertz and more.
*	- details about a-trous algorithm for spatial denoising filter
*/
2021-01-02 10:42:02 -08:00

93 lines
2.5 KiB
Bash

/*
* Copyright 2021 elven cache. All rights reserved.
* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
*/
#ifndef NORMAL_ENCODING_SH
#define NORMAL_ENCODING_SH
#define NE_USE_OCTAHEDRAL_REPRESENTATION 0
// From "A Survey of Efficient Representations for Independent Unit Vectors"
// http://jcgt.org/published/0003/02/01/paper.pdf
// Convert an oct24 (2x12bit normal) to an rgb8 value for storing in texture
vec3 snorm12x2_to_unorm8x3 (vec2 f) {
f = clamp(f, -1.0, 1.0);//min(max(f, vec2(-1.0)), vec2(1.0));
vec2 u = floor(f * 2047.0 + 2047.5);
float t = floor(u.y / 256.0);
// "This code assumes that rounding will occur during storage."
// -- Not certain but this appears to mainly apply to the x channel.
// From paper: x = u.x / 16.0 - 0.5
// Instead round by +0.5 and floor.
return vec3(floor(u.x / 16.0), fract(u.x / 16.0) * 256.0 + t, u.y - t * 256.0) / 255.0;
}
// Unpack oct24 (2x12bit normal) from an rgb8 value stored in texture (normal spec)
vec2 unorm8x3_to_snorm12x2 (vec3 u) {
u *= 255.0;
u.y *= (1.0 / 16.0);
vec2 s = vec2(u.x * 16.0 + floor(u.y), fract(u.y) * (16.0 * 256.0) + u.z);
s = s * (1.0 / 2047.0) - 1.0;
return min(max(s, -1.0), 1.0);
}
// Built in sign test could return 0, don't want that
vec2 signNotZero (vec2 v) {
return vec2((v.x >= 0.0) ? 1.0 : -1.0, (v.y >= 0.0) ? 1.0 : -1.0);
}
// Assume normalized input. Output is (-1, 1) for each component
vec2 float32x3_to_oct(vec3 v) {
// Project the sphere onto the octahedron, and then onto the xy plane
vec2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z)));
// Reflect the folds of the lower hemisphere over the diagonals
return (v.z <= 0.0) ? ((1.0 - abs(p.yx)) * signNotZero(p)) : p;
}
// Get a float3 normal from an oct representation
vec3 oct_to_float32x3 (vec2 e) {
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
if (v.z < 0.0) {
v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
}
return normalize(v);
}
vec3 SignedNormalEncodeToOct (vec3 normal) {
return snorm12x2_to_unorm8x3(float32x3_to_oct(normal));
}
vec3 SignedNormalDecodeFromOct (vec3 normal) {
return oct_to_float32x3(unorm8x3_to_snorm12x2(normal));
}
vec3 NormalEncode (vec3 normal)
{
#if NE_USE_OCTAHEDRAL_REPRESENTATION
return SignedNormalEncodeToOct(normal);
#else
return normal * 0.5 + 0.5;
#endif
}
vec3 NormalDecode (vec3 normal)
{
#if NE_USE_OCTAHEDRAL_REPRESENTATION
return SignedNormalDecodeFromOct(normal);
#else
return normal * 2.0 - 1.0;
#endif
}
#endif // NORMAL_ENCODING_SH