e5d6a5a22b
* add denoise example /* * 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 */ * screen space shadows sample implement screen space shadows. requires deferred rendering or a depth prepass. convert rendered depth to linear depth to skip reconstructing multiple times when doing shadow test. project light into screen space to find direction from each pixel to the light. walk through screen space texture towards light. sample depth to reconstruct position represented by this sample pixel and compare to position along interpolated ray from pixel to light. if position represented by depth is closer to the eye than the light ray, an initial pixel is in shadow. specify distance of shadow ray via world units or pixels in screen space. optionally offset the initial sample position by noise to reduce banding. demonstrate other ways to reduce hard edge of screen space shadow. * clean out denoise sample for pull request... * rename folder to 44- add missing file
112 lines
3.0 KiB
Scala
112 lines
3.0 KiB
Scala
$input v_texcoord0
|
|
|
|
/*
|
|
* Copyright 2021 elven cache. All rights reserved.
|
|
* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
|
|
*/
|
|
|
|
#include "../common/common.sh"
|
|
#include "parameters.sh"
|
|
|
|
SAMPLER2D(s_depth, 0);
|
|
|
|
#define DEPTH_EPSILON 1e-4
|
|
|
|
// from assao sample, cs_assao_prepare_depths.sc
|
|
vec3 NDCToViewspace( vec2 pos, float viewspaceDepth )
|
|
{
|
|
vec3 ret;
|
|
|
|
ret.xy = (u_ndcToViewMul * pos.xy + u_ndcToViewAdd) * viewspaceDepth;
|
|
|
|
ret.z = viewspaceDepth;
|
|
|
|
return ret;
|
|
}
|
|
|
|
float ShadertoyNoise (vec2 uv) {
|
|
return fract(sin(dot(uv.xy, vec2(12.9898,78.233))) * 43758.5453123);
|
|
}
|
|
|
|
void main()
|
|
{
|
|
vec2 texCoord = v_texcoord0;
|
|
float linearDepth = texture2D(s_depth, texCoord).x;
|
|
vec3 viewSpacePosition = NDCToViewspace(texCoord, linearDepth);
|
|
|
|
vec3 lightStep = normalize(u_lightPosition - viewSpacePosition);
|
|
|
|
// screen space radius not usable directly. convert value given in pixels,
|
|
// to world units. this is important later when comparing depth in world units
|
|
float radius = u_shadowRadius;
|
|
if (0.0 < u_useScreenSpaceRadius)
|
|
{
|
|
// is there a better way to do this calculation?
|
|
float radiusTexCoordX = u_shadowRadius * u_viewTexel.x + texCoord.x;
|
|
float radiusPositionX = u_ndcToViewMul.x * radiusTexCoordX + u_ndcToViewAdd.x;
|
|
radius = abs(radiusPositionX * linearDepth - viewSpacePosition.x);
|
|
}
|
|
lightStep *= (radius / u_shadowSteps);
|
|
|
|
vec3 samplePosition = viewSpacePosition;
|
|
float random = ShadertoyNoise(gl_FragCoord.xy + vec2(314.0, 159.0)*u_frameIdx);
|
|
float initialOffset = (0.0 < u_useNoiseOffset) ? (0.5+random) : 1.0;
|
|
samplePosition += initialOffset * lightStep;
|
|
|
|
mat4 viewToProj = mat4(
|
|
u_viewToProj0,
|
|
u_viewToProj1,
|
|
u_viewToProj2,
|
|
u_viewToProj3
|
|
);
|
|
|
|
float occluded = 0.0;
|
|
float softOccluded = 0.0;
|
|
float firstHit = u_shadowSteps;
|
|
for (int i = 0; i < int(u_shadowSteps); ++i, samplePosition += lightStep)
|
|
{
|
|
vec3 psSamplePosition = instMul(viewToProj, vec4(samplePosition, 1.0)).xyw;
|
|
psSamplePosition.xy *= (1.0/psSamplePosition.z);
|
|
|
|
vec2 sampleCoord = psSamplePosition.xy * 0.5 + 0.5;
|
|
sampleCoord.y = 1.0 - sampleCoord.y;
|
|
|
|
// using texture2Dlod because dx9 compiler doesn't like
|
|
// gradient instructions within this loop
|
|
float sampleDepth = texture2DLod(s_depth, sampleCoord, 0).x;
|
|
|
|
float delta = (samplePosition.z - sampleDepth);
|
|
if (DEPTH_EPSILON < delta && delta < radius)
|
|
{
|
|
firstHit = min(firstHit, float(i));
|
|
// for hard, soft occlusion
|
|
occluded += 1.0;
|
|
// for very soft occlusion
|
|
softOccluded += saturate(radius - delta);
|
|
}
|
|
}
|
|
|
|
float shadow;
|
|
if (1.5 < u_contactShadowsMode)
|
|
{
|
|
// very soft occlusion, includes distance falloff above
|
|
shadow = softOccluded * (1.0 - (firstHit / u_shadowSteps));
|
|
shadow = 1.0 - saturate(shadow);
|
|
shadow = shadow*shadow;
|
|
}
|
|
else if (0.5 < u_contactShadowsMode)
|
|
{
|
|
// soft occlusion
|
|
shadow = occluded * (1.0 - (firstHit / u_shadowSteps));
|
|
shadow = 1.0 - saturate(shadow);
|
|
shadow = shadow*shadow;
|
|
}
|
|
else // == 0
|
|
{
|
|
// hard occlusion
|
|
shadow = 0.0 < occluded ? 0.0 : 1.0;
|
|
}
|
|
|
|
gl_FragColor = vec4_splat(shadow);
|
|
}
|