Cull invisible gaussian splat chunks in prepare pass

This commit is contained in:
2026-04-11 14:22:51 +08:00
parent c543ccf79c
commit 2fb6eca854
3 changed files with 177 additions and 2 deletions

View File

@@ -107,6 +107,102 @@ Shader "Builtin Gaussian Splat Utilities"
return rawValue ^ mask;
}
float2 UnpackHalfRange(uint packedValue)
{
return float2(
f16tof32(packedValue & 0xffffu),
f16tof32(packedValue >> 16u));
}
float4 TransformLocalPointToClip(
float3 localPosition,
float4x4 modelMatrix,
float4x4 viewMatrix,
float4x4 projectionMatrix)
{
const float3 worldPosition = mul(modelMatrix, float4(localPosition, 1.0)).xyz;
const float3 viewPosition = mul(viewMatrix, float4(worldPosition, 1.0)).xyz;
return mul(projectionMatrix, float4(viewPosition, 1.0));
}
bool IsChunkDefinitelyOutsideFrustum(
GaussianSplatChunkData chunkData,
uint chunkCount,
uint chunkIndex,
float4x4 modelMatrix,
float4x4 viewMatrix,
float4x4 projectionMatrix)
{
if (chunkCount == 0u || chunkIndex >= chunkCount)
{
return false;
}
float3 localMin = float3(chunkData.posX.x, chunkData.posY.x, chunkData.posZ.x);
float3 localMax = float3(chunkData.posX.y, chunkData.posY.y, chunkData.posZ.y);
if (localMin.x > localMax.x || localMin.y > localMax.y || localMin.z > localMax.z)
{
return true;
}
// Inflate by a conservative 3-sigma envelope derived from the chunk's maximum splat scale.
const float3 maxScale = float3(
UnpackHalfRange(chunkData.sclX).y,
UnpackHalfRange(chunkData.sclY).y,
UnpackHalfRange(chunkData.sclZ).y);
const float radius = max(maxScale.x, max(maxScale.y, maxScale.z)) * 3.0;
localMin -= radius.xxx;
localMax += radius.xxx;
bool allBehind = true;
bool anyBehind = false;
bool outsideLeft = true;
bool outsideRight = true;
bool outsideBottom = true;
bool outsideTop = true;
bool outsideNear = true;
bool outsideFar = true;
[unroll]
for (uint cornerIndex = 0u; cornerIndex < 8u; ++cornerIndex)
{
const float3 localCorner = float3(
(cornerIndex & 1u) != 0u ? localMax.x : localMin.x,
(cornerIndex & 2u) != 0u ? localMax.y : localMin.y,
(cornerIndex & 4u) != 0u ? localMax.z : localMin.z);
const float4 clipCorner = TransformLocalPointToClip(
localCorner,
modelMatrix,
viewMatrix,
projectionMatrix);
const bool behind = clipCorner.w <= 0.0;
allBehind = allBehind && behind;
anyBehind = anyBehind || behind;
if (!behind)
{
outsideLeft = outsideLeft && (clipCorner.x < -clipCorner.w);
outsideRight = outsideRight && (clipCorner.x > clipCorner.w);
outsideBottom = outsideBottom && (clipCorner.y < -clipCorner.w);
outsideTop = outsideTop && (clipCorner.y > clipCorner.w);
outsideNear = outsideNear && (clipCorner.z < 0.0);
outsideFar = outsideFar && (clipCorner.z > clipCorner.w);
}
}
if (allBehind)
{
return true;
}
if (anyBehind)
{
return false;
}
return outsideLeft || outsideRight || outsideBottom || outsideTop || outsideNear || outsideFar;
}
float3x3 CalcMatrixFromRotationScale(float4 rotation, float3 scale)
{
const float x = rotation.x;
@@ -260,7 +356,9 @@ Shader "Builtin Gaussian Splat Utilities"
const GaussianSplatOtherData otherData = GaussianSplatOther[index];
const float4 colorOpacity = GaussianSplatColor[index];
const GaussianSplatSHData shData = GaussianSplatSH[index];
const GaussianSplatChunkData chunkData = GaussianSplatChunks[index / GAUSSIAN_SPLAT_CHUNK_SIZE];
const uint chunkIndex = index / GAUSSIAN_SPLAT_CHUNK_SIZE;
const uint chunkCount = (uint)gSplatParams.w;
const GaussianSplatChunkData chunkData = GaussianSplatChunks[chunkIndex];
const uint shOrder = min((uint)gSplatParams.z, 3u);
if (chunkData.posX.x > chunkData.posX.y ||
chunkData.posY.x > chunkData.posY.y ||
@@ -270,6 +368,18 @@ Shader "Builtin Gaussian Splat Utilities"
GaussianSplatViewDataBuffer[index] = viewData;
return;
}
if (IsChunkDefinitelyOutsideFrustum(
chunkData,
chunkCount,
chunkIndex,
gModelMatrix,
gViewMatrix,
gProjectionMatrix))
{
GaussianSplatSortDistances[index] = 0xffffffffu;
GaussianSplatViewDataBuffer[index] = viewData;
return;
}
const float3 worldCenter = mul(gModelMatrix, float4(localCenter, 1.0)).xyz;
const float3 viewCenter = mul(gViewMatrix, float4(worldCenter, 1.0)).xyz;

View File

@@ -1106,7 +1106,7 @@ bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
static_cast<float>(cachedGaussianSplat->splatCount),
static_cast<float>(workingSet->sortCapacity),
shOrder,
0.0f)
static_cast<float>(cachedGaussianSplat->chunkCount))
};
if (passLayout->descriptorSetCount > 0u) {