From 66cfd30d3e8d33e48c211a0f37a7de78726fdacb Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 12 Jan 2025 21:37:13 -0800 Subject: [PATCH] SSR: reworked temporal accumulation --- Shaders/Common/public/PostFX_Common.fxh | 26 ++---- Shaders/Common/public/ShaderUtilities.fxh | 33 +++++++ .../SSR_ComputeTemporalAccumulation.fx | 90 +++++++++---------- 3 files changed, 85 insertions(+), 64 deletions(-) diff --git a/Shaders/Common/public/PostFX_Common.fxh b/Shaders/Common/public/PostFX_Common.fxh index 41c7f07e..89f77ca5 100644 --- a/Shaders/Common/public/PostFX_Common.fxh +++ b/Shaders/Common/public/PostFX_Common.fxh @@ -144,24 +144,16 @@ float SampleCameraZFromDepthUC(Texture2D DepthBuffer, int MipLevel, float4x4 Proj) { - Location -= float2(0.5, 0.5); + int4 FetchCoords; + float4 Weights; + GetBilinearSamplingInfoUC(Location, Dimensions, FetchCoords, Weights); + + float Z00 = DepthToCameraZ(DepthBuffer.Load(int3(FetchCoords.xy, MipLevel)), Proj); + float Z10 = DepthToCameraZ(DepthBuffer.Load(int3(FetchCoords.zy, MipLevel)), Proj); + float Z01 = DepthToCameraZ(DepthBuffer.Load(int3(FetchCoords.xw, MipLevel)), Proj); + float Z11 = DepthToCameraZ(DepthBuffer.Load(int3(FetchCoords.zw, MipLevel)), Proj); - float2 Location00 = floor(Location); - float2 Weights = Location - Location00; - - int i0 = clamp(int(Location00.x), 0, Dimensions.x - 1); - int j0 = clamp(int(Location00.y), 0, Dimensions.y - 1); - int i1 = clamp(int(Location00.x) + 1, 0, Dimensions.x - 1); - int j1 = clamp(int(Location00.y) + 1, 0, Dimensions.y - 1); - - float Z00 = DepthToCameraZ(DepthBuffer.Load(int3(i0, j0, MipLevel)), Proj); - float Z10 = DepthToCameraZ(DepthBuffer.Load(int3(i1, j0, MipLevel)), Proj); - float Z01 = DepthToCameraZ(DepthBuffer.Load(int3(i0, j1, MipLevel)), Proj); - float Z11 = DepthToCameraZ(DepthBuffer.Load(int3(i1, j1, MipLevel)), Proj); - - return lerp(lerp(Z00, Z10, Weights.x), - lerp(Z01, Z11, Weights.x), - Weights.y); + return dot(float4(Z00, Z10, Z01, Z11), Weights); } float SampleCameraZFromDepthUC(Texture2D DepthBuffer, diff --git a/Shaders/Common/public/ShaderUtilities.fxh b/Shaders/Common/public/ShaderUtilities.fxh index 8b308ca0..ae918868 100644 --- a/Shaders/Common/public/ShaderUtilities.fxh +++ b/Shaders/Common/public/ShaderUtilities.fxh @@ -108,4 +108,37 @@ void BasisFromNormal(in float3 N, T = normalize(cross(N, abs(N.y) > 0.5 ? float3(1.0, 0.0, 0.0) : float3(0.0, 1.0, 0.0))); B = cross(T, N); } + +/// Returns bilinear sampling information for unnormailzed coordinates. +/// +/// \param [in] Location - Unnormalized location in the texture space. +/// \param [in] Dimensions - Texture dimensions. +/// \param [out] FetchCoords - Texture coordinates to fetch the data from: +/// (x, y) - lower left corner +/// (z, w) - upper right corner +/// \param [out] Weights - Bilinear interpolation weights. +/// +/// \remarks The filtering should be done as follows: +/// Tex.Load(FetchCoords.xy) * Weights.x + +/// Tex.Load(FetchCoords.zy) * Weights.y + +/// Tex.Load(FetchCoords.xw) * Weights.z + +/// Tex.Load(FetchCoords.zw) * Weights.w +void GetBilinearSamplingInfoUC(in float2 Location, + in int2 Dimensions, + out int4 FetchCoords, + out float4 Weights) +{ + Location -= float2(0.5, 0.5); + float2 Location00 = floor(Location); + + FetchCoords.xy = int2(Location00); + FetchCoords.zw = FetchCoords.xy + int2(1, 1); + Dimensions -= int2(1, 1); + FetchCoords = clamp(FetchCoords, int4(0, 0, 0, 0), Dimensions.xyxy); + + float x = Location.x - Location00.x; + float y = Location.y - Location00.y; + Weights = float4(1.0 - x, x, 1.0 - x, x) * float4(1.0 - y, 1.0 - y, y, y); +} + #endif //_SHADER_UTILITIES_FXH_ diff --git a/Shaders/PostProcess/ScreenSpaceReflection/private/SSR_ComputeTemporalAccumulation.fx b/Shaders/PostProcess/ScreenSpaceReflection/private/SSR_ComputeTemporalAccumulation.fx index 472c01a1..4b839d5a 100644 --- a/Shaders/PostProcess/ScreenSpaceReflection/private/SSR_ComputeTemporalAccumulation.fx +++ b/Shaders/PostProcess/ScreenSpaceReflection/private/SSR_ComputeTemporalAccumulation.fx @@ -161,64 +161,60 @@ ProjectionDesc ComputeReprojection(float2 PrevPos, float CurrDepth) g_TextureCurrDepth.GetDimensions(DepthDim.x, DepthDim.y); if (!Desc.IsSuccess) { - float Disocclusion = 0.0; - const int SearchRadius = 1; + float4 BestWeights = float4(0.0, 0.0, 0.0, 0.0); + int4 BestFetchCoords = int4(0, 0, 0, 0); + float BestTotalWeight = 0.0; + + const int SearchRadius = 1; + const float BestTotalWeightEarlyExitThreshold = 0.9; for (int y = -SearchRadius; y <= SearchRadius; y++) { for (int x = -SearchRadius; x <= SearchRadius; x++) { - float2 Location = PrevPos + float2(x, y); - float PrevCameraZ = SampleCameraZFromDepthUC(g_TexturePrevDepth, DepthDim, Location, 0, g_PrevCamera.mProj); - float Weight = ComputeDisocclusion(CurrCamZ, PrevCameraZ); - if (Weight > Disocclusion) + float2 Location = PrevPos + float2(x, y); + + int4 FetchCoords; + float4 Weights; + GetBilinearSamplingInfoUC(Location, DepthDim, FetchCoords, Weights); + + float PrevZ00 = DepthToCameraZ(LoadPrevDepth(FetchCoords.xy), g_PrevCamera.mProj); + float PrevZ10 = DepthToCameraZ(LoadPrevDepth(FetchCoords.zy), g_PrevCamera.mProj); + float PrevZ01 = DepthToCameraZ(LoadPrevDepth(FetchCoords.xw), g_PrevCamera.mProj); + float PrevZ11 = DepthToCameraZ(LoadPrevDepth(FetchCoords.zw), g_PrevCamera.mProj); + + Weights.x *= ComputeDisocclusion(CurrCamZ, PrevZ00) > (SSR_DISOCCLUSION_THRESHOLD / 2.0) ? 1.0 : 0.0; + Weights.y *= ComputeDisocclusion(CurrCamZ, PrevZ10) > (SSR_DISOCCLUSION_THRESHOLD / 2.0) ? 1.0 : 0.0; + Weights.z *= ComputeDisocclusion(CurrCamZ, PrevZ01) > (SSR_DISOCCLUSION_THRESHOLD / 2.0) ? 1.0 : 0.0; + Weights.w *= ComputeDisocclusion(CurrCamZ, PrevZ11) > (SSR_DISOCCLUSION_THRESHOLD / 2.0) ? 1.0 : 0.0; + + float TotalWeight = dot(Weights, float4(1.0, 1.0, 1.0, 1.0)); + if (TotalWeight > BestTotalWeight) { - Disocclusion = Weight; - Desc.PrevCoord = Location; + BestTotalWeight = TotalWeight; + BestWeights = Weights; + BestFetchCoords = FetchCoords; + Desc.PrevCoord = Location; + + if (BestTotalWeight > BestTotalWeightEarlyExitThreshold) + break; } } + + if (BestTotalWeight > BestTotalWeightEarlyExitThreshold) + break; } - Desc.IsSuccess = Disocclusion > SSR_DISOCCLUSION_THRESHOLD; - Desc.Color = SamplePrevRadianceLinear(Desc.PrevCoord); - } - - if (!Desc.IsSuccess) - { - float2 PrevCoord = Desc.PrevCoord - float2(0.5, 0.5); - float2 PrevCoord00 = floor(PrevCoord); - int2 PrevCoord00i = int2(PrevCoord00); - float x = PrevCoord.x - PrevCoord00.x; - float y = PrevCoord.y - PrevCoord00.y; - - float Weight[4]; - Weight[0] = (1.0 - x) * (1.0 - y); - Weight[1] = x * (1.0 - y); - Weight[2] = (1.0 - x) * y; - Weight[3] = x * y; - - float WeightSum = 0.0; - float WeightedCamZ = 0.0; - float4 WeightedColor = float4(0.0, 0.0, 0.0, 0.0); - for (int SampleIdx = 0; SampleIdx < 4; ++SampleIdx) + Desc.IsSuccess = BestTotalWeight > 0.1; + if (Desc.IsSuccess) { - int2 Location = PrevCoord00i + int2(SampleIdx & 0x01, SampleIdx >> 1); - float PrevCamZ = DepthToCameraZ(LoadPrevDepth(Location), g_PrevCamera.mProj); - - bool IsValidSample = ComputeDisocclusion(CurrCamZ, PrevCamZ) > (SSR_DISOCCLUSION_THRESHOLD / 2.0); - Weight[SampleIdx] *= float(IsValidSample); - - WeightedColor += Weight[SampleIdx] * LoadPrevRadiance(Location); - WeightedCamZ += Weight[SampleIdx] * PrevCamZ; - WeightSum += Weight[SampleIdx]; + Desc.Color = ( + LoadPrevRadiance(BestFetchCoords.xy) * BestWeights.x + + LoadPrevRadiance(BestFetchCoords.zy) * BestWeights.y + + LoadPrevRadiance(BestFetchCoords.xw) * BestWeights.z + + LoadPrevRadiance(BestFetchCoords.zw) * BestWeights.w + ) / BestTotalWeight; } - - WeightSum = max(WeightSum, 1e-6); - WeightedCamZ /= WeightSum; - WeightedColor /= WeightSum; - - Desc.IsSuccess = ComputeDisocclusion(CurrCamZ, WeightedCamZ) > SSR_DISOCCLUSION_THRESHOLD; - Desc.Color = WeightedColor; - } + } Desc.IsSuccess = Desc.IsSuccess && IsInsideScreen(Desc.PrevCoord, g_CurrCamera.f4ViewportSize.xy); return Desc;