Files
maszyna/betterRenderer/shaders/contact_shadows.hlsl
2025-04-15 01:32:56 +02:00

150 lines
4.9 KiB
HLSL

Texture2D<float> g_DepthTexture : register(t0);
RWTexture2D<float> g_Output : register(u0);
sampler g_SamplerLinearClamp : register(s0);
// From https://www.shadertoy.com/view/3tB3z3 - except we're using R2 here
#define XE_HILBERT_LEVEL 6U
#define XE_HILBERT_WIDTH ( (1U << XE_HILBERT_LEVEL) )
#define XE_HILBERT_AREA ( XE_HILBERT_WIDTH * XE_HILBERT_WIDTH )
inline uint HilbertIndex( uint posX, uint posY )
{
uint index = 0U;
for( uint curLevel = XE_HILBERT_WIDTH/2U; curLevel > 0U; curLevel /= 2U )
{
uint regionX = ( posX & curLevel ) > 0U;
uint regionY = ( posY & curLevel ) > 0U;
index += curLevel * curLevel * ( (3U * regionX) ^ regionY);
if( regionY == 0U )
{
if( regionX == 1U )
{
posX = uint( (XE_HILBERT_WIDTH - 1U) ) - posX;
posY = uint( (XE_HILBERT_WIDTH - 1U) ) - posY;
}
uint temp = posX;
posX = posY;
posY = temp;
}
}
return index;
}
// Engine-specific screen & temporal noise loader
float2 SpatioTemporalNoise( uint2 pixCoord, uint temporalIndex ) // without TAA, temporalIndex is always 0
{
float2 noise;
#if 1 // Hilbert curve driving R2 (see https://www.shadertoy.com/view/3tB3z3)
#ifdef XE_GTAO_HILBERT_LUT_AVAILABLE // load from lookup texture...
uint index = g_srcHilbertLUT.Load( uint3( pixCoord % 64, 0 ) ).x;
#else // ...or generate in-place?
uint index = HilbertIndex( pixCoord.x, pixCoord.y );
#endif
index += 288*(temporalIndex%64); // why 288? tried out a few and that's the best so far (with XE_HILBERT_LEVEL 6U) - but there's probably better :)
// R2 sequence - see http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
return float2( frac( 0.5 + index * float2(0.75487766624669276005, 0.5698402909980532659114) ) );
#else // Pseudo-random (fastest but looks bad - not a good choice)
uint baseHash = Hash32( pixCoord.x + (pixCoord.y << 15) );
baseHash = Hash32Combine( baseHash, temporalIndex );
return float2( Hash32ToFloat( baseHash ), Hash32ToFloat( Hash32( baseHash ) ) );
#endif
}
cbuffer DrawConstants : register(b0) {
float4x4 g_Projection;
float4x4 g_InverseProjection;
float3 g_LightDirView;
float g_NumSamples;
float g_SampleRange;
float g_Thickness;
uint g_FrameIndex;
}
uint2 NdcToPixel(in float2 size, in float4 ndc);
float2 NdcToUv(in float4 ndc);
float4 PixelToNdc(in float2 size, in uint2 pixel, in float depth);
float4 UvToNdc(in float2 uv, in float depth);
uint2 ViewToPixel(in float2 size, in float3 view);
float2 ViewToUv(in float3 view);
float GetPixelDepth(in float2 size, in uint2 pixel);
float GetUvDepth(in float2 uv);
float3 PixelToViewWithDepth(in float2 size, in uint2 pixel);
float3 UvToViewWithDepth(in float2 uv);
[numthreads(8, 8, 1)]
void main(uint3 PixCoord : SV_DispatchThreadID) {
float2 size;
g_DepthTexture.GetDimensions(size.x, size.y);
uint2 pixel = PixCoord.xy;
float occlusion = 0;
g_Output[pixel] = 1.;
float2 noise = SpatioTemporalNoise(PixCoord.xy, g_FrameIndex);
float3 viewPosition = PixelToViewWithDepth(size, pixel);
float3 sample = viewPosition;
float3 step = g_LightDirView * g_SampleRange / g_NumSamples;
sample += noise.x * step;
for(int i = 0; i < g_NumSamples; ++i) {
sample += step;
float2 sample_uv = ViewToUv(sample);
if(all(and(sample_uv > 0, sample_uv < 1.))) {
float depthDelta = sample.z * .998 - GetUvDepth(sample_uv);
if(depthDelta < 0. && depthDelta > -g_Thickness) {
occlusion = 1.;
break;
}
}
}
g_Output[pixel] = 1. - occlusion;
}
uint2 NdcToPixel(in float2 size, in float4 ndc) {
return (uint2)floor(NdcToUv(ndc) * size);
}
float2 NdcToUv(in float4 ndc) {
return ndc.xy * float2(.5, -.5) + .5;
}
float4 PixelToNdc(in float2 size, in uint2 pixel, in float ndc_depth) {
return float4(float2(2., -2.) * ((((float2)pixel + .5) / size) - .5), ndc_depth, 1.);
}
float4 UvToNdc(in float2 uv, in float ndc_depth) {
return float4(float2(2., -2.) * (uv - .5), ndc_depth, 1.);
}
uint2 ViewToPixel(in float2 size, in float3 view) {
float4 ndc = mul(g_Projection, float4(view, 1.));
ndc /= ndc.w;
return NdcToPixel(size, ndc);
}
float2 ViewToUv(in float3 view) {
float4 ndc = mul(g_Projection, float4(view, 1.));
ndc /= ndc.w;
return NdcToUv(ndc);
}
float GetPixelDepth(in float2 size, in uint2 pixel) {
return PixelToViewWithDepth(size, pixel).z;
}
float GetUvDepth(in float2 uv) {
return UvToViewWithDepth(uv).z;
}
float3 PixelToViewWithDepth(in float2 size, in uint2 pixel) {
float4 ndc = PixelToNdc(size, pixel, g_DepthTexture[pixel]);
ndc = mul(g_InverseProjection, ndc);
return ndc.xyz / ndc.w;
}
float3 UvToViewWithDepth(in float2 uv) {
float4 ndc = UvToNdc(uv, g_DepthTexture.SampleLevel(g_SamplerLinearClamp, uv, 0.));
ndc = mul(g_InverseProjection, ndc);
return ndc.xyz / ndc.w;
}