#pragma kernel CSMain #if 1 // Includes are available only when HDRP is installed #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" #else // Copied here from above includes, to avoid compilation errors in the absence of HDRP. // This is not currently working, for reasons TBD. float4 _ScreenSize; float4 _ZBufferParams; Texture2DArray _CameraDepthTexture; #define LOAD_TEXTURE2D_X_LOD(textureName, unCoord2, lod) textureName.Load(int4(unCoord2, 0, lod)) float LinearEyeDepth(float depth, float4 zBufferParam) { return 1.0 / (zBufferParam.z * depth + zBufferParam.w); } #endif // 16 samples, won't handle a different number static const uint kSampleCount = 16; static const float2 kDiskKernel[kSampleCount] = { float2(0, 0), float2(0.54545456, 0), float2(0.16855472, 0.5187581), float2(-0.44128203, 0.3206101), float2(-0.44128197, -0.3206102), float2(0.1685548, -0.5187581), float2(1, 0), float2(0.809017, 0.58778524), float2(0.30901697, 0.95105654), float2(-0.30901703, 0.9510565), float2(-0.80901706, 0.5877852), float2(-1, 0), float2(-0.80901694, -0.58778536), float2(-0.30901664, -0.9510566), float2(0.30901712, -0.9510565), float2(0.80901694, -0.5877853), }; // Kernel above taken from PostProcessing/Shaders/Builtins/DiskKernels.hlsl struct FocusDistanceParams { uint VoteBias; // 0...15 float DepthTolerance; // 0.02 float SampleRadius; // 0.02 float SamplePosX; // 0 float SamplePosY; // 0 float DefaultFocusDistance; // current focus distance }; struct FocusDistanceOutput { float FocusDistance; }; RWStructuredBuffer _FocusDistanceParams; // : register(u2); RWStructuredBuffer _FocusDistanceOutput; // : register(u3); [numthreads(1, 1, 1)] void CSMain(uint3 id : SV_DispatchThreadID) { // Settings FocusDistanceParams params = _FocusDistanceParams[0]; float sampleKernelSize = params.SampleRadius; float depthTolerance = params.DepthTolerance; uint voteBias = params.VoteBias; float2 kernelPos = float2(params.SamplePosX, params.SamplePosY); // Buckets float depths[kSampleCount] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }; uint votes[kSampleCount] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // Init the first bucket with the current target depth // Even though it might actually not be hit by any sample, and // in a rare case cause the last sample to not get a bucket. // No votes for now. depths[0] = params.DefaultFocusDistance; uint mostVotes = 0; uint biggestBucket = 0; for (uint i = 0; i < kSampleCount; i++) { float2 offset = kernelPos + kDiskKernel[i] * sampleKernelSize + float2(0.5, 0.5); float depth = LOAD_TEXTURE2D_X_LOD(_CameraDepthTexture, int2(offset.xy * _ScreenSize.xy), 0).r; // Convert to distance units depth = LinearEyeDepth(depth, _ZBufferParams); // TODO: Any depth that would result in effective focus at infinity should be // clamped here into one value. Otherwise we're unnecesarily spreading them over buckets // and decreasing their voting power. Need to figure out what that focus distance is from DoF params. // depth = min(_FocusDistanceToInfinity, depth); // Alternatively: bucket based on raw depth and only convert the output to linear. // Find an empty bucket or add to a bucket that's close enough for (uint j = 0; j < kSampleCount; j++) { float bucket = depths[j]; // New bucket, claim it if (bucket < 0) { depths[j] = depth; votes[j] += 1; if (votes[j] > mostVotes) { mostVotes = votes[j]; biggestBucket = j; } break; } // Belongs to this bucket, upvote if (abs(bucket - depth) <= depthTolerance) { votes[j] += 1; if (votes[j] > mostVotes) { mostVotes = votes[j]; biggestBucket = j; } break; } } } // If the bucket with the most votes got considerably more votes (i.e. more by voteBias) than // the current target focus distance, set it as the new target focus distance. // Clamp the vote bias to the most votes value - if the buckets are too small, we can't be too sticky. float targetFocusDistance = params.DefaultFocusDistance; voteBias = min(mostVotes - 1, voteBias); if (mostVotes > votes[0] + voteBias) targetFocusDistance = depths[biggestBucket]; _FocusDistanceOutput[0].FocusDistance = targetFocusDistance; }