Giant_Beast_2025/Plugins/MetaXR/Shaders/Private/ScreenPSEnvironmentDepthMinMax.usf

104 lines
4.0 KiB
Plaintext

// Copyright Epic Games, Inc. All Rights Reserved.
#include "/Engine/Private/Common.ush"
Texture2DArray InTexture;
SamplerState InTextureSampler;
#if !ENABLE_MULTI_VIEW
// Use shader constants on PC Link
int ArraySlice;
#endif
void Main(
FScreenVertexOutput Input,
#if ENABLE_MULTI_VIEW
// Use Multi View on Mobile
uint ArraySlice : SV_ViewID,
#endif
out float4 OutColor : SV_Target0
)
{
float3 Dimensions;
InTexture.GetDimensions(Dimensions.x, Dimensions.y, Dimensions.z);
float2 onePixelOffset = 1.0f / Dimensions.xy;
const uint NUM_SAMPLES = 4U;
const float2 offsets[NUM_SAMPLES] = {
float2(-1.0f, 1.0f),
float2(1.0f, 1.0f),
float2(-1.0f, -1.0f),
float2(1.0f, -1.0f)
};
float4 depths[NUM_SAMPLES];
float minDepth = 1.0f;
float maxDepth = 0.0f;
float depthSum = 0.0f;
// Find the local min and max, and collect all depth samples in the sampling grid
uint i;
UNROLL
for (i = 0U; i < NUM_SAMPLES; ++i) {
float2 uvSample = Input.UV + (offsets[i] + 0.5f) * onePixelOffset;
float4 depth4 = InTexture.Gather(InTextureSampler, float3(uvSample, ArraySlice));
depthSum += dot(depth4, float4(0.25f, 0.25, 0.25, 0.25));
float localMax = max(max(depth4.x, depth4.y), max(depth4.z, depth4.w));
float localMin = min(min(depth4.x, depth4.y), min(depth4.z, depth4.w));
maxDepth = max(maxDepth, localMax);
minDepth = min(minDepth, localMin);
depths[i] = depth4;
}
float maxSumDepth = 0.0f;
float minSumDepth = 0.0f;
float maxSumCount = 0.0f;
float minSumCount = 0.0f;
// Model the entire neighborhood as a bimodal distribution aggregated around the minimum and maximum values.
// Each side of the distribution (min and max) accepts values in a multiplicative range with respect to metric depth
// This will therefore aggregate all depth values until a maximum slope (depending also on the depth resolution)
static const float kMaxMetricDepthThrMultiplier = 0.85f;
static const float kMinMetricDepthThrMultiplier = 1.15f;
// Compute thresholds in window depth space:
// Dmetric = 1 / (1 - Dwin)
// Tmetric = kMultiplier * Dmetric
// Twin = 1 - 1/Tmetric
// Therefore:
// Twin = 1 - 1/(kMultiplier * (1 / (1 - Dwin))) = 1 - (1 - Dwin) / kMultiplier = 1 - 1/kMultiplier + Dwin / kMultiplier
float depthThrMax = (1.0f - 1.0f / kMaxMetricDepthThrMultiplier) + maxDepth * (1.0f / kMaxMetricDepthThrMultiplier);
float depthThrMin = (1.0f - 1.0f / kMinMetricDepthThrMultiplier) + minDepth * (1.0f / kMinMetricDepthThrMultiplier);
float avg = depthSum * (1.0f / float(NUM_SAMPLES));
if (depthThrMax < minDepth && depthThrMin > maxDepth) {
// Degenerate case: the entire neighborhood is within min-max thresholds for averaging
// therefore minAvg == maxAvg == avg.
// Directly output the encoded fragColor as:
// (1 - minAvg, 1 - maxAvg, avg - minAvg, maxAvg - minAvg)
OutColor = float4(1.0f - avg, 1.0f - avg, 0.0f, 0.0f);
} else {
// Compute average depths around the minimum and maximum values
UNROLL
for (i = 0U; i < NUM_SAMPLES; ++i) {
float4 maxMask = (depths[i] >= float4(depthThrMax, depthThrMax, depthThrMax, depthThrMax));
float4 minMask = (depths[i] <= float4(depthThrMin, depthThrMin, depthThrMin, depthThrMin));
minSumDepth += dot(minMask, depths[i]);
minSumCount += dot(minMask, float4(1.0f, 1.0f, 1.0f, 1.0f));
maxSumDepth += dot(maxMask, depths[i]);
maxSumCount += dot(maxMask, float4(1.0f, 1.0f, 1.0f, 1.0f));
}
float minAvg = minSumDepth / minSumCount;
float maxAvg = maxSumDepth / maxSumCount;
// Encoding the depth as a 4-channel RGBA image for improved numerical stability.
// minAvg and maxAvg are encoded in inverse range to use more floating point precision in the far field.
// The interpolation ratio between min and max is computed as: (avg - minAvg) / (maxAvg - minAvg)
// We can perform the differences here at higher precision
// We let the division later to the occlusion shader to preserve the bilinear interpolation properties as with minAvg and maxAvg.
OutColor = float4(1.0f - minAvg, 1.0f - maxAvg, avg - minAvg, maxAvg - minAvg);
}
}