Files
FlaxEngine/Source/Shaders/SSR.shader
2024-02-26 19:00:48 +01:00

293 lines
10 KiB
GLSL

// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "./Flax/Common.hlsl"
#include "./Flax/LightingCommon.hlsl"
#include "./Flax/ReflectionsCommon.hlsl"
#include "./Flax/SSR.hlsl"
#include "./Flax/GBuffer.hlsl"
#include "./Flax/GlobalSignDistanceField.hlsl"
#include "./Flax/GI/GlobalSurfaceAtlas.hlsl"
// Enable/disable luminance filter to reduce reflections highlights
#define SSR_REDUCE_HIGHLIGHTS 1
// Enable/disable blurring SSR during sampling results and mixing with reflections buffer
#define SSR_MIX_BLUR 1
META_CB_BEGIN(0, Data)
GBufferData GBuffer;
float MaxColorMiplevel;
float TraceSizeMax;
float MaxTraceSamples;
float RoughnessFade;
float2 SSRtexelSize;
float TemporalTime;
float BRDFBias;
float WorldAntiSelfOcclusionBias;
float EdgeFadeFactor;
float TemporalResponse;
float TemporalScale;
float RayTraceStep;
float TemporalEffect;
float Intensity;
float FadeOutDistance;
float4x4 ViewMatrix;
float4x4 ViewProjectionMatrix;
GlobalSDFData GlobalSDF;
GlobalSurfaceAtlasData GlobalSurfaceAtlas;
META_CB_END
DECLARE_GBUFFERDATA_ACCESS(GBuffer)
Texture2D Texture0 : register(t4);
Texture2D Texture1 : register(t5);
Texture2D Texture2 : register(t6);
#if USE_GLOBAL_SURFACE_ATLAS
Texture3D<float> GlobalSDFTex : register(t7);
Texture3D<float> GlobalSDFMip : register(t8);
ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t9);
ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t10);
Buffer<float4> GlobalSurfaceAtlasObjects : register(t11);
Texture2D GlobalSurfaceAtlasDepth : register(t12);
Texture2D GlobalSurfaceAtlasTex : register(t13);
#endif
// Pixel Shader for screen space reflections rendering - combine pass
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_CombinePass(Quad_VS2PS input) : SV_Target0
{
// Inputs:
// Texture0 - light buffer
// Texture1 - reflections buffer
// Texture2 - PreIntegratedGF
// Sample GBuffer and light buffer
GBufferData gBufferData = GetGBufferData();
GBufferSample gBuffer = SampleGBuffer(gBufferData, input.TexCoord);
float4 light = SAMPLE_RT(Texture0, input.TexCoord);
// Check if can light pixel
BRANCH
if (gBuffer.ShadingModel != SHADING_MODEL_UNLIT)
{
// Sample reflections buffer
float3 reflections = SAMPLE_RT(Texture1, input.TexCoord).rgb;
// Calculate specular color
float3 specularColor = GetSpecularColor(gBuffer);
if (gBuffer.Metalness < 0.001)
specularColor = 0.04f * gBuffer.Specular;
// Calculate reflection color
float3 V = normalize(gBufferData.ViewPos - gBuffer.WorldPos);
float NoV = saturate(dot(gBuffer.Normal, V));
light.rgb += reflections * EnvBRDF(Texture2, specularColor, gBuffer.Roughness, NoV) * gBuffer.AO;
}
return light;
}
// Pixel Shader for screen space reflections rendering - ray trace pass
META_PS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_1(USE_GLOBAL_SURFACE_ATLAS=0)
META_PERMUTATION_1(USE_GLOBAL_SURFACE_ATLAS=1)
float4 PS_RayTracePass(Quad_VS2PS input) : SV_Target0
{
// Inputs:
// Texture0 - color buffer (rgb color, with mip maps chain or without)
// SRV 7-8 Global SDF
// SRV 9-13 Global Surface Atlas
// Base layer color with reflections from probes but empty alpha so SSR blur will have valid bacground values to smooth with
float4 base = float4(Texture0.SampleLevel(SamplerLinearClamp, input.TexCoord, 0).rgb, 0);
// Sample GBuffer
GBufferData gBufferData = GetGBufferData();
GBufferSample gBuffer = SampleGBuffer(gBufferData, input.TexCoord);
// Reject invalid pixels
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT || gBuffer.Roughness > RoughnessFade || gBuffer.ViewPos.z > FadeOutDistance)
return base;
// Trace depth buffer to find intersection
float3 screenHit = TraceScreenSpaceReflection(input.TexCoord, gBuffer, Depth, gBufferData.ViewPos, ViewMatrix, ViewProjectionMatrix, RayTraceStep, MaxTraceSamples, TemporalEffect, TemporalTime, WorldAntiSelfOcclusionBias, BRDFBias, FadeOutDistance, RoughnessFade, EdgeFadeFactor);
float4 result = base;
if (screenHit.z > 0)
{
float3 viewVector = normalize(gBufferData.ViewPos - gBuffer.WorldPos);
float NdotV = saturate(dot(gBuffer.Normal, viewVector));
float coneTangent = lerp(0.0, gBuffer.Roughness * 5 * (1.0 - BRDFBias), pow(NdotV, 1.5) * sqrt(gBuffer.Roughness));
float intersectionCircleRadius = coneTangent * length(screenHit.xy - input.TexCoord);
float mip = clamp(log2(intersectionCircleRadius * TraceSizeMax), 0.0, MaxColorMiplevel);
float3 sampleColor = Texture0.SampleLevel(SamplerLinearClamp, screenHit.xy, mip).rgb;
result = float4(sampleColor, screenHit.z);
if (screenHit.z >= 0.9f)
return result;
}
// Calculate reflection direction (the same TraceScreenSpaceReflection)
float3 reflectWS = ScreenSpaceReflectionDirection(input.TexCoord, gBuffer, gBufferData.ViewPos, TemporalEffect, TemporalTime, BRDFBias);
// Fallback to Global SDF and Global Surface Atlas tracing
#if USE_GLOBAL_SURFACE_ATLAS && CAN_USE_GLOBAL_SURFACE_ATLAS
GlobalSDFTrace sdfTrace;
float maxDistance = 100000;
float selfOcclusionBias = GlobalSDF.CascadeVoxelSize[0];
sdfTrace.Init(gBuffer.WorldPos + gBuffer.Normal * selfOcclusionBias, reflectWS, 0.0f, maxDistance);
GlobalSDFHit sdfHit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, sdfTrace);
if (sdfHit.IsHit())
{
float3 hitPosition = sdfHit.GetHitPosition(sdfTrace);
float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(GlobalSDF, sdfHit);
float4 surfaceAtlas = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, RWGlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hitPosition, -reflectWS, surfaceThreshold);
result = lerp(surfaceAtlas, float4(result.rgb, 1), result.a);
}
#endif
return result;
}
#ifndef RESOLVE_SAMPLES
#define RESOLVE_SAMPLES 1
#endif
// Pixel Shader for screen space reflections rendering - resolve pass
META_PS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_1(RESOLVE_SAMPLES=1)
META_PERMUTATION_1(RESOLVE_SAMPLES=2)
META_PERMUTATION_1(RESOLVE_SAMPLES=4)
META_PERMUTATION_1(RESOLVE_SAMPLES=8)
float4 PS_ResolvePass(Quad_VS2PS input) : SV_Target0
{
static const float2 Offsets[8] =
{
float2( 0, 0),
float2( 2, -2),
float2(-2, -2),
float2( 0, 2),
float2(-2, 0),
float2( 0, -2),
float2( 2, 0),
float2( 2, 2),
};
float2 uv = input.TexCoord;
// Inputs:
// Texture0 - ray trace buffer (xy: HDR color, z: weight)
// Sample GBuffer
GBufferData gBufferData = GetGBufferData();
GBufferSample gBuffer = SampleGBuffer(gBufferData, uv);
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT)
return 0;
// Randomize it a little
float2 random = RandN2(uv + TemporalTime);
float2 blueNoise = random.xy * 2.0 - 1.0;
float2x2 offsetRotationMatrix = float2x2(blueNoise.x, blueNoise.y, -blueNoise.y, blueNoise.x);
// Resolve samples
float4 result = 0.0;
UNROLL
for (int i = 0; i < RESOLVE_SAMPLES; i++)
{
float2 offsetUV = Offsets[i] * SSRtexelSize;
offsetUV = mul(offsetRotationMatrix, offsetUV);
float4 value = Texture0.SampleLevel(SamplerLinearClamp, uv + offsetUV, 0);
#if SSR_REDUCE_HIGHLIGHTS
value.rgb /= 1 + Luminance(value.rgb);
#endif
result += value;
}
// Calculate final result value
result /= RESOLVE_SAMPLES;
result.a *= Intensity;
#if SSR_REDUCE_HIGHLIGHTS
result.rgb /= 1 - Luminance(result.rgb);
#endif
//return float4(abs(Luminance(result) - Luminance(saturate(result))).xxx, 1);
//return saturate(result);
return result;
}
// Pixel Shader for screen space reflections rendering - temporal pass
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_TemporalPass(Quad_VS2PS input) : SV_Target0
{
// Inputs:
// Texture0 - resolved SSR reflections buffer
// Texture1 - prev frame temporal SSR buffer
// Texture2 - motion vectors
float2 uv = input.TexCoord;
// Sample velocity
float2 velocity = Texture2.SampleLevel(SamplerLinearClamp, uv, 0).xy;
// Prepare
float2 prevUV = uv - velocity;
float4 current = Texture0.SampleLevel(SamplerLinearClamp, uv, 0);
float4 previous = Texture1.SampleLevel(SamplerLinearClamp, prevUV, 0);
float2 du = float2(SSRtexelSize.x, 0.0);
float2 dv = float2(0.0, SSRtexelSize.y);
// Sample pixels around
float4 currentTopLeft = Texture0.SampleLevel(SamplerLinearClamp, uv.xy - dv - du, 0);
float4 currentTopCenter = Texture0.SampleLevel(SamplerLinearClamp, uv.xy - dv, 0);
float4 currentTopRight = Texture0.SampleLevel(SamplerLinearClamp, uv.xy - dv + du, 0);
float4 currentMiddleLeft = Texture0.SampleLevel(SamplerLinearClamp, uv.xy - du, 0);
float4 currentMiddleCenter = Texture0.SampleLevel(SamplerLinearClamp, uv.xy, 0);
float4 currentMiddleRight = Texture0.SampleLevel(SamplerLinearClamp, uv.xy + du, 0);
float4 currentBottomLeft = Texture0.SampleLevel(SamplerLinearClamp, uv.xy + dv - du, 0);
float4 currentBottomCenter = Texture0.SampleLevel(SamplerLinearClamp, uv.xy + dv, 0);
float4 currentBottomRight = Texture0.SampleLevel(SamplerLinearClamp, uv.xy + dv + du, 0);
float4 currentMin = min(currentTopLeft, min(currentTopCenter, min(currentTopRight, min(currentMiddleLeft, min(currentMiddleCenter, min(currentMiddleRight, min(currentBottomLeft, min(currentBottomCenter, currentBottomRight))))))));
float4 currentMax = max(currentTopLeft, max(currentTopCenter, max(currentTopRight, max(currentMiddleLeft, max(currentMiddleCenter, max(currentMiddleRight, max(currentBottomLeft, max(currentBottomCenter, currentBottomRight))))))));
float scale = TemporalScale;
float4 center = (currentMin + currentMax) * 0.5f;
currentMin = (currentMin - center) * scale + center;
currentMax = (currentMax - center) * scale + center;
previous = clamp(previous, currentMin, currentMax);
current = clamp(current, 0, HDR_CLAMP_MAX);
float response = TemporalResponse * (1 - length(velocity) * 8);
return lerp(current, previous, saturate(response));
}
// Pixel Shader for screen space reflections rendering - mix pass
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_MixPass(Quad_VS2PS input) : SV_Target0
{
// Inputs:
// Texture0 - final SSR reflections buffer
float4 ssr = Texture0.SampleLevel(SamplerLinearClamp, input.TexCoord, 0);
#if SSR_MIX_BLUR
ssr += Texture0.SampleLevel(SamplerLinearClamp, input.TexCoord + float2(0, SSRtexelSize.y), 0);
ssr += Texture0.SampleLevel(SamplerLinearClamp, input.TexCoord - float2(0, SSRtexelSize.y), 0);
ssr += Texture0.SampleLevel(SamplerLinearClamp, input.TexCoord + float2(SSRtexelSize.x, 0), 0);
ssr += Texture0.SampleLevel(SamplerLinearClamp, input.TexCoord - float2(SSRtexelSize.x, 0), 0);
ssr *= (1.0f / 5.0f);
#endif
return ssr;
}