Finish up the HizCulling

This commit is contained in:
2025-02-22 23:01:42 +09:00
parent f0dbf8e581
commit 833502f87c
12 changed files with 136 additions and 129 deletions

View File

@@ -0,0 +1,128 @@
// 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"
#define FLT_MIN 1.175494351e-38 // Minimum representable positive floating-point number
#define FLT_MAX 3.402823466e+38 // Maximum representable floating-point number
StructuredBuffer<OrientedBoundingBox> _VolumeBounds;
StructuredBuffer<int2> _DepthPyramidMipLevelOffsets;
uint _FullVolumeCount;
uint _DepthPyramidMaxMip;
RWByteAddressBuffer _VisibleVolumeCount : register(u0);
RWByteAddressBuffer _VisibleVolumeIndices : register(u1);
RW_TEXTURE2D_X(float, _DebugTexture);
float4 ComputeScreenPos(float4 pos, float projectionSign)
{
float4 o = pos * 0.5f;
o.xy = float2(o.x, o.y * projectionSign) + o.w;
o.zw = pos.zw;
return o;
}
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;
}
float GetOccluderDepth(float2 screenMin, float2 screenMax)
{
float rectWidth = (screenMax.x - screenMin.x);
float rectHeight = (screenMax.y - screenMin.y);
float rectSize = max(rectWidth, rectHeight);
int maxMipLevel = min(_DepthPyramidMaxMip, 14);
int mipLevel = clamp((int)ceil(log2(rectSize)), 0, maxMipLevel);
float4 boxUV = float4(screenMin, screenMax);
const int depthSampleCount = 5;
float occluderDepth[depthSampleCount];
occluderDepth[0] = SampleDepthLod(boxUV.xy, mipLevel);
occluderDepth[1] = SampleDepthLod(boxUV.xw, mipLevel);
occluderDepth[2] = SampleDepthLod(boxUV.zy, mipLevel);
occluderDepth[3] = SampleDepthLod(boxUV.zw, mipLevel);
occluderDepth[4] = SampleDepthLod((screenMin + screenMax) * 0.5, mipLevel);
float minDepth = 1.0;
for (int i = 0; i < depthSampleCount; ++i)
{
minDepth = min(minDepth, occluderDepth[i]);
}
return minDepth;
}
[numthreads(64,1,1)]
void CSMain(uint3 dispatchThreadId : SV_DispatchThreadID)
{
if (dispatchThreadId.x >= _FullVolumeCount)
{
return; // early exit if outside our range
}
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[8];
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 minimum depth (closest point)
float2 screenMin = float2(FLT_MAX, FLT_MAX);
float2 screenMax = float2(-FLT_MAX, -FLT_MAX);
float boxMaxDepth = -FLT_MAX;
[unroll]
for (int j = 0; j < 8; ++j)
{
float3 cornerRWS = GetCameraRelativePositionWS(corners[j]);
float4 positionCS = TransformWorldToHClip(cornerRWS);
positionCS /= positionCS.w;
float2 screenPos = ComputeScreenPos(positionCS, _ProjectionParams.x).xy * _ScreenSize.xy;
screenMin = min(screenMin, screenPos);
screenMax = max(screenMax, screenPos);
boxMaxDepth = max(boxMaxDepth, positionCS.z);
}
float occluderDepth = GetOccluderDepth(screenMin, screenMax);
// Perform the occlusion test:
// If the closest point of the box (boxMaxDepth) is behind the occluder,
// then the box is completely occluded.
// TODO: pack 16 bits index to save memory
if (occluderDepth <= boxMaxDepth)
{
uint index;
_VisibleVolumeCount.InterlockedAdd(0, 1, index);
_VisibleVolumeIndices.Store(index << 2, dispatchThreadId.x);
}
}