183 lines
8.8 KiB
HLSL
183 lines
8.8 KiB
HLSL
#ifndef __culling_hlsl_
|
|
#define __culling_hlsl_
|
|
|
|
#include "Params.hlsl"
|
|
|
|
inline void CalculateBoundingBox(in float4x4 objectTransformMatrix, inout float4 BoundingBox[8])
|
|
{
|
|
// Calculate clip space matrix
|
|
float4x4 to_clip_space_mat = mul(mvpMatrix, objectTransformMatrix);
|
|
|
|
float3 Min = boundsCenter - boundsExtents;
|
|
float3 Max = boundsCenter + boundsExtents;
|
|
|
|
// Transform all 8 corner points of the object bounding box to clip space
|
|
BoundingBox[0] = mul(to_clip_space_mat, float4(Min.x, Max.y, Min.z, 1.0));
|
|
BoundingBox[1] = mul(to_clip_space_mat, float4(Min.x, Max.y, Max.z, 1.0));
|
|
BoundingBox[2] = mul(to_clip_space_mat, float4(Max.x, Max.y, Max.z, 1.0));
|
|
BoundingBox[3] = mul(to_clip_space_mat, float4(Max.x, Max.y, Min.z, 1.0));
|
|
BoundingBox[4] = mul(to_clip_space_mat, float4(Max.x, Min.y, Min.z, 1.0));
|
|
BoundingBox[5] = mul(to_clip_space_mat, float4(Max.x, Min.y, Max.z, 1.0));
|
|
BoundingBox[6] = mul(to_clip_space_mat, float4(Min.x, Min.y, Max.z, 1.0));
|
|
BoundingBox[7] = mul(to_clip_space_mat, float4(Min.x, Min.y, Min.z, 1.0));
|
|
}
|
|
|
|
inline bool IsFrustumCulled(float4 BoundingBox[8])
|
|
{
|
|
bool isCulled = false;
|
|
// Test all 8 points with both positive and negative planes
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
// cull if outside positive plane:
|
|
isCulled = isCulled ||
|
|
(BoundingBox[0][i] > BoundingBox[0].w + frustumOffset &&
|
|
BoundingBox[1][i] > BoundingBox[1].w + frustumOffset &&
|
|
BoundingBox[2][i] > BoundingBox[2].w + frustumOffset &&
|
|
BoundingBox[3][i] > BoundingBox[3].w + frustumOffset &&
|
|
BoundingBox[4][i] > BoundingBox[4].w + frustumOffset &&
|
|
BoundingBox[5][i] > BoundingBox[5].w + frustumOffset &&
|
|
BoundingBox[6][i] > BoundingBox[6].w + frustumOffset &&
|
|
BoundingBox[7][i] > BoundingBox[7].w + frustumOffset);
|
|
|
|
// cull if outside negative plane:
|
|
isCulled = isCulled ||
|
|
(BoundingBox[0][i] < -BoundingBox[0].w - frustumOffset &&
|
|
BoundingBox[1][i] < -BoundingBox[1].w - frustumOffset &&
|
|
BoundingBox[2][i] < -BoundingBox[2].w - frustumOffset &&
|
|
BoundingBox[3][i] < -BoundingBox[3].w - frustumOffset &&
|
|
BoundingBox[4][i] < -BoundingBox[4].w - frustumOffset &&
|
|
BoundingBox[5][i] < -BoundingBox[5].w - frustumOffset &&
|
|
BoundingBox[6][i] < -BoundingBox[6].w - frustumOffset &&
|
|
BoundingBox[7][i] < -BoundingBox[7].w - frustumOffset);
|
|
}
|
|
|
|
return isCulled;
|
|
}
|
|
|
|
inline float OcclusionSample(float4 BoundingRect, float LOD, float xOffset)
|
|
{
|
|
// Fetch the depth texture and sample it with the bounds
|
|
// Middle Point
|
|
float MaxDepth = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(((BoundingRect.z - BoundingRect.x) / 2.0) + BoundingRect.x + xOffset, ((BoundingRect.w - BoundingRect.y) / 2.0) + BoundingRect.y), LOD).r;
|
|
|
|
// Corner Points
|
|
float4 Samples;
|
|
Samples.x = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(BoundingRect.x + xOffset, BoundingRect.y), LOD).r;
|
|
Samples.y = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(BoundingRect.x + xOffset, BoundingRect.w), LOD).r;
|
|
Samples.z = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(BoundingRect.z + xOffset, BoundingRect.w), LOD).r;
|
|
Samples.w = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(BoundingRect.z + xOffset, BoundingRect.y), LOD).r;
|
|
MaxDepth = max(max(max(Samples.x, Samples.y), max(Samples.z, Samples.w)), MaxDepth);
|
|
|
|
if (occlusionAccuracy >= 2)
|
|
{
|
|
// 1/4 Points
|
|
float xShift = (BoundingRect.z - BoundingRect.x) / 4.0;
|
|
float yShift = (BoundingRect.w - BoundingRect.y) / 4.0;
|
|
Samples.x = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(xShift + BoundingRect.x + xOffset, yShift + BoundingRect.y), LOD).r;
|
|
Samples.y = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(xShift * 3 + BoundingRect.x + xOffset, yShift + BoundingRect.y), LOD).r;
|
|
Samples.z = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(xShift + BoundingRect.x + xOffset, yShift * 3 + BoundingRect.y), LOD).r;
|
|
Samples.w = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(xShift * 3 + BoundingRect.x + xOffset, yShift * 3 + BoundingRect.y), LOD).r;
|
|
MaxDepth = max(max(max(Samples.x, Samples.y), max(Samples.z, Samples.w)), MaxDepth);
|
|
|
|
|
|
if (occlusionAccuracy >= 3)
|
|
{
|
|
// 1/8 Points
|
|
xShift = (BoundingRect.z - BoundingRect.x) / 8.0;
|
|
yShift = (BoundingRect.w - BoundingRect.y) / 8.0;
|
|
Samples.x = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(xShift + BoundingRect.x + xOffset, yShift + BoundingRect.y), LOD).r;
|
|
Samples.y = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(xShift * 7 + BoundingRect.x + xOffset, yShift + BoundingRect.y), LOD).r;
|
|
Samples.z = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(xShift + BoundingRect.x + xOffset, yShift * 7 + BoundingRect.y), LOD).r;
|
|
Samples.w = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(xShift * 7 + BoundingRect.x + xOffset, yShift * 7 + BoundingRect.y), LOD).r;
|
|
MaxDepth = max(max(max(Samples.x, Samples.y), max(Samples.z, Samples.w)), MaxDepth);
|
|
|
|
// 3/8 Points
|
|
Samples.x = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(xShift * 3 + BoundingRect.x + xOffset, yShift * 3 + BoundingRect.y), LOD).r;
|
|
Samples.y = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(xShift * 5 + BoundingRect.x + xOffset, yShift * 3 + BoundingRect.y), LOD).r;
|
|
Samples.z = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(xShift * 3 + BoundingRect.x + xOffset, yShift * 5 + BoundingRect.y), LOD).r;
|
|
Samples.w = 1 - hiZMap.SampleLevel(sampler_hiZMap, float2(xShift * 5 + BoundingRect.x + xOffset, yShift * 5 + BoundingRect.y), LOD).r;
|
|
MaxDepth = max(max(max(Samples.x, Samples.y), max(Samples.z, Samples.w)), MaxDepth);
|
|
}
|
|
}
|
|
|
|
return MaxDepth;
|
|
}
|
|
|
|
inline bool IsOcclusionCulled(float4 BoundingBox[8])
|
|
{
|
|
// NOTE: for Direct3D, the clipping space z coordinate ranges from 0 to w and for OpenGL, it ranges from -w to w. However, since we use Unity's Projection Matrix directly,
|
|
// there is no need to worry about the difference between platforms. The projection matrix will always be left handed.
|
|
// Also, the reversed depth value between these APIs are taken care of in the blit and compute shaders while creating the hiZ depth texture.
|
|
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
BoundingBox[i].xyz /= BoundingBox[i].w; // unscale clip depth to NDC
|
|
BoundingBox[i].z = BoundingBox[i].z * 0.5 + 0.5; // map BB depth values back to [0, 1];
|
|
}
|
|
|
|
float4 BoundingRect;
|
|
|
|
BoundingRect.x = min(min(min(BoundingBox[0].x, BoundingBox[1].x),
|
|
min(BoundingBox[2].x, BoundingBox[3].x)),
|
|
min(min(BoundingBox[4].x, BoundingBox[5].x),
|
|
min(BoundingBox[6].x, BoundingBox[7].x))) / 2.0 + 0.5;
|
|
BoundingRect.y = min(min(min(BoundingBox[0].y, BoundingBox[1].y),
|
|
min(BoundingBox[2].y, BoundingBox[3].y)),
|
|
min(min(BoundingBox[4].y, BoundingBox[5].y),
|
|
min(BoundingBox[6].y, BoundingBox[7].y))) / 2.0 + 0.5;
|
|
BoundingRect.z = max(max(max(BoundingBox[0].x, BoundingBox[1].x),
|
|
max(BoundingBox[2].x, BoundingBox[3].x)),
|
|
max(max(BoundingBox[4].x, BoundingBox[5].x),
|
|
max(BoundingBox[6].x, BoundingBox[7].x))) / 2.0 + 0.5;
|
|
BoundingRect.w = max(max(max(BoundingBox[0].y, BoundingBox[1].y),
|
|
max(BoundingBox[2].y, BoundingBox[3].y)),
|
|
max(max(BoundingBox[4].y, BoundingBox[5].y),
|
|
max(BoundingBox[6].y, BoundingBox[7].y))) / 2.0 + 0.5;
|
|
|
|
float InstanceDepth = min(min(min(BoundingBox[0].z, BoundingBox[1].z),
|
|
min(BoundingBox[2].z, BoundingBox[3].z)),
|
|
min(min(BoundingBox[4].z, BoundingBox[5].z),
|
|
min(BoundingBox[6].z, BoundingBox[7].z)));
|
|
// Calculate the bounding rectangle size in viewport coordinates
|
|
float ViewSizeX = (BoundingRect.z - BoundingRect.x) * hiZTxtrSize.x;
|
|
float ViewSizeY = (BoundingRect.w - BoundingRect.y) * hiZTxtrSize.y;
|
|
|
|
// Calculate the texture LOD used for lookup in the depth buffer texture
|
|
float LOD = ceil(log2(max(ViewSizeX, ViewSizeY) / pow(2, occlusionAccuracy)));
|
|
float MaxDepth = OcclusionSample(BoundingRect, LOD, 0);
|
|
|
|
return InstanceDepth > MaxDepth + occlusionOffset;
|
|
}
|
|
|
|
inline void IsCulled(in float4x4 instanceMatrix, in float dist, out bool culled)
|
|
{
|
|
culled = false;
|
|
|
|
// Distance culling
|
|
if (dist >= maxDistance || dist < minDistance)
|
|
{
|
|
culled = true;
|
|
}
|
|
|
|
if (!culled && dist >= minCullingDistance)
|
|
{
|
|
float4 BoundingBox[8];
|
|
CalculateBoundingBox(instanceMatrix, BoundingBox);
|
|
|
|
// OBB Frustum Culling
|
|
if (isFrustumCulling)
|
|
{
|
|
culled = IsFrustumCulled(BoundingBox);
|
|
}
|
|
|
|
// Hierarchical Z-Buffer Occlusion Culling
|
|
if (!culled && isOcclusionCulling)
|
|
{
|
|
culled = IsOcclusionCulled(BoundingBox);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|