104 lines
4.0 KiB
Plaintext
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);
|
|
}
|
|
}
|