// Each #kernel tells which function to compile; you can have many kernels #pragma kernel CSMain #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" #include "Packages/com.misaki.ao-volume/Runtime/Shader/Includes/GeometryData.cs.hlsl" #include "Packages/com.misaki.ao-volume/Runtime/Shader/Includes/Common.hlsl" #define _FLT_MIN 1.175494351e-38 // Minimum representable positive floating-point number #define _FLT_MAX 3.402823466e+38 // Maximum representable floating-point number #define _CORNERS_COUNT 8 uint _FullVolumeCount; uint _DepthPyramidMaxMip; StructuredBuffer _VolumeBounds; StructuredBuffer _DepthPyramidMipLevelOffsets; RWByteAddressBuffer _VisibleVolumeCount : register(u0); RWByteAddressBuffer _VisibleVolumeIndices : register(u1); float SampleDepthLod(int2 uv, int lod) { int2 mipCoord = uv >> lod; int2 mipOffset = _DepthPyramidMipLevelOffsets[lod]; float deviceDepth = LOAD_TEXTURE2D_X(_CameraDepthTexture, mipOffset + mipCoord).r; return deviceDepth; } [numthreads(64,1,1)] void CSMain(uint3 dispatchThreadId : SV_DispatchThreadID) { if (dispatchThreadId.x >= _FullVolumeCount) { return; } OrientedBoundingBox box = _VolumeBounds[dispatchThreadId.x]; // Compute the 8 corners of the OBB. // The box is defined by its center, two axes (right & up) and its extents. // We compute the forward vector as the cross product (assuming right and up are orthogonal). float3 rightExtent = box.right * box.extent.x; float3 upExtent = box.up * box.extent.y; float3 forward = normalize(cross(box.right, box.up)); float3 forwardExtent = forward * box.extent.z; float3 corners[_CORNERS_COUNT]; corners[0] = box.center + rightExtent + upExtent + forwardExtent; corners[1] = box.center + rightExtent + upExtent - forwardExtent; corners[2] = box.center + rightExtent - upExtent + forwardExtent; corners[3] = box.center + rightExtent - upExtent - forwardExtent; corners[4] = box.center - rightExtent + upExtent + forwardExtent; corners[5] = box.center - rightExtent + upExtent - forwardExtent; corners[6] = box.center - rightExtent - upExtent + forwardExtent; corners[7] = box.center - rightExtent - upExtent - forwardExtent; // Compute screen-space bounding rectangle and find the maximum depth (closest point) float2 screenMin = float2(_FLT_MAX, _FLT_MAX); float2 screenMax = float2(-_FLT_MAX, -_FLT_MAX); float boxMaxDepth = 0.0; float2 cornerPositionSS[_CORNERS_COUNT]; [unroll] for (int i = 0; i < _CORNERS_COUNT; i++) { float3 cornerRWS = GetCameraRelativePositionWS(corners[i]); float4 positionCS = TransformWorldToHClip(cornerRWS); positionCS /= positionCS.w; float2 positionNDC = ComputePositionNDC(positionCS, _ProjectionParams.x).xy; screenMin = min(screenMin, positionNDC); screenMax = max(screenMax, positionNDC); boxMaxDepth = max(boxMaxDepth, positionCS.z); cornerPositionSS[i] = positionNDC * _ScreenSize.xy; } float rectWidth = (screenMax.x - screenMin.x); float rectHeight = (screenMax.y - screenMin.y); float rectSize = max(rectWidth, rectHeight); int maxMipLevel = min(_DepthPyramidMaxMip, 14); int mipLevel = (int)ceil((float)maxMipLevel - max(log2(1.0 / (rectSize * rectSize)), 0)); [unroll] for (int j = 0; j < _CORNERS_COUNT; j++) { float2 uv = cornerPositionSS[j]; float occluderDepth = SampleDepthLod(uv, mipLevel); // Perform the occlusion test: // If the closest point of the box (boxMaxDepth) is behind the occluder, then the box is completely occluded. // Note that depth buffer in hdrp is stored in a non-normalized reversed range [0, 1] (0 is far, 1 is near). // TODO: pack 16 bits index to save memory if (occluderDepth <= boxMaxDepth) { uint index; _VisibleVolumeCount.InterlockedAdd(0, 1, index); _VisibleVolumeIndices.Store(index << 2, dispatchThreadId.x); break; } } }