Shader "Occlusion/Soft/DepthPreprocessing" { SubShader { Pass { Name "Environment Depth Preprocessing Pass" ZWrite Off CGPROGRAM #pragma target 4.5 #pragma vertex vert #pragma fragment frag #pragma multi_compile _ XR_LINEAR_DEPTH #include "Utils.hlsl" Texture2DArray_half _EnvironmentDepthTexture; SamplerState sampler_EnvironmentDepthTexture; float4 _EnvironmentDepthTexture_TexelSize; struct Attributes { uint vertexId : SV_VertexID; uint instanceId : SV_InstanceID; }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; uint depthSlice : SV_RenderTargetArrayIndex; }; float4 CalculateMinMaxDepth(float2 uv, float slice) { const int NUM_SAMPLES = 5; static const float2 offsets[NUM_SAMPLES] = { float2(2.0f, 0.0f), float2(0.0f, 2.0f), float2(-2.0f, 0.0f), float2(0.0f, 0.0f), float2(0.0f, -2.0f) }; float4 depths[NUM_SAMPLES]; const float2 onePixelOffset = _EnvironmentDepthTexture_TexelSize.xy; float minDepth = 1.0f; float maxDepth = 0.0f; float depthSum = 0.0f; int sampleIndex = 0; // Find the local min and max, and collect all depth samples in the sampling grid while (sampleIndex < NUM_SAMPLES) { float2 uvSample = uv + (offsets[sampleIndex] + 0.5f) * onePixelOffset; depths[sampleIndex] = _EnvironmentDepthTexture.Gather(sampler_EnvironmentDepthTexture, float3(uvSample.x, uvSample.y, slice)); #ifdef XR_LINEAR_DEPTH depths[sampleIndex].x = ConvertDepthToNonSymmetricRange(LinearDepthToSymmetricRangeNDC(depths[sampleIndex].x)); depths[sampleIndex].y = ConvertDepthToNonSymmetricRange(LinearDepthToSymmetricRangeNDC(depths[sampleIndex].y)); depths[sampleIndex].z = ConvertDepthToNonSymmetricRange(LinearDepthToSymmetricRangeNDC(depths[sampleIndex].z)); depths[sampleIndex].w = ConvertDepthToNonSymmetricRange(LinearDepthToSymmetricRangeNDC(depths[sampleIndex].w)); #endif depthSum += dot(depths[sampleIndex], float4(0.25f, 0.25, 0.25, 0.25)); float localMax = max(max(depths[sampleIndex].x, depths[sampleIndex].y), max(depths[sampleIndex].z, depths[sampleIndex].w)); float localMin = min(min(depths[sampleIndex].x, depths[sampleIndex].y), min(depths[sampleIndex].z, depths[sampleIndex].w)); maxDepth = max(maxDepth, localMax); minDepth = min(minDepth, localMin); sampleIndex++; } float maxSumDepth = 0.0f; float minSumDepth = 0.0f; float maxSumCount = 0.0f; float minSumCount = 0.0f; const float kMaxDepthMultiplier = 0.8846f; // depths scaled symmetric to 1.15 scale. 1-1/1.15==0.13 k=1/(1+0.13)==0.8846 const float kMinDepthMultiplier = 1.15f; const float normalDepthScale = 1.0f; const float depthReciprocalScaleMax = 1.0f / kMaxDepthMultiplier; const float depthReciprocalScaleMin = 1.0f / kMinDepthMultiplier; const float depthMaxBase = 1.0f - normalDepthScale * depthReciprocalScaleMax; const float depthMinBase = 1.0f - normalDepthScale * depthReciprocalScaleMin; // Adjusting depth thresholds by applying reciprocal scaling to modify depth impact float depthThrMax = depthMaxBase + maxDepth * depthReciprocalScaleMax; float depthThrMin = depthMinBase + minDepth * depthReciprocalScaleMin; 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) return float4(1.0f - avg, 1.0f - avg, 0.0f, 0.0f); } sampleIndex = 0; while (sampleIndex < NUM_SAMPLES) { float4 maxMask = depths[sampleIndex] >= float4(depthThrMax, depthThrMax, depthThrMax, depthThrMax); float4 minMask = depths[sampleIndex] <= float4(depthThrMin, depthThrMin, depthThrMin, depthThrMin); minSumDepth += dot(minMask, depths[sampleIndex]); minSumCount += dot(minMask, float4(1.0f, 1.0f, 1.0f, 1.0f)); maxSumDepth += dot(maxMask, depths[sampleIndex]); maxSumCount += dot(maxMask, float4(1.0f, 1.0f, 1.0f, 1.0f)); // Value used to interpolate occlusion alphas between min and max values sampleIndex++; } float minAvg = minSumDepth / minSumCount; float maxAvg = maxSumDepth / maxSumCount; return float4(1 - minAvg, 1 - maxAvg, avg - minAvg, maxAvg - minAvg); } Varyings vert(const Attributes input) { Varyings output; const uint vertexID = input.vertexId; const float2 uv = float2(vertexID << 1 & 2, vertexID & 2); output.uv = float2(uv.x, 1.0 - uv.y); output.positionCS = float4(uv * 2.0 - 1.0, 1.0, 1.0); output.depthSlice = input.instanceId; return output; } float4 frag(const Varyings input) : SV_Target { return CalculateMinMaxDepth(input.uv, input.depthSlice); } ENDCG } } }