Singularity/Library/PackageCache/com.unity.render-pipelines..../Editor/Lighting/ProbeVolume/ProbeVolumeSubdivide.compute
2024-05-06 11:45:45 -07:00

272 lines
7.0 KiB
Plaintext

#pragma kernel Clear
#pragma kernel ClearBuffer
#pragma kernel FillUVMap
#pragma kernel JumpFlooding
#pragma kernel FinalPass
#pragma kernel VoxelizeProbeVolumeData
#pragma kernel Subdivide
#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch
// #pragma enable_d3d11_debug_symbols
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
#include "Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbePlacement.cs.hlsl"
Texture3D _Input;
Texture3D _ProbeVolumeData;
RWTexture3D<float4> _Output;
RWTexture3D<float4> _FinalOutput;
AppendStructuredBuffer<float3> _Bricks;
RWStructuredBuffer<float3> _BricksToClear;
StructuredBuffer<GPUProbeVolumeOBB> _ProbeVolumes;
float4 _Size;
float4 _MaxBrickSize;
float4 _VolumeSizeInBricks;
float4 _VolumeOffsetInBricks;
float4 _VolumeWorldOffset;
float4 _SDFSize;
float _Offset;
float _BrickSize;
float _SubdivisionLevel;
float _MaxSubdivisionLevel;
float _ProbeVolumeCount;
float _ClearValue;
[numthreads(8,8,1)]
void Clear(uint3 id : SV_DispatchThreadID)
{
if (any(id >= uint3(_Size.xyz)))
return;
_Output[id] = _ClearValue;
}
[numthreads(64,1,1)]
void ClearBuffer(uint3 id : SV_DispatchThreadID)
{
_BricksToClear[id.x] = 0;
}
[numthreads(8,8,1)]
void FillUVMap(uint3 id : SV_DispatchThreadID)
{
float4 input = _Input[id];
if (input.a > 0.5)
{
_Output[id] = float4(id / _Size.x, 1);
}
else
{
_Output[id] = 0; // mark UV as invalid with w = 0
}
}
static float3 offset3D[27] =
{
float3(-1, -1, -1),
float3(-1, 0, -1),
float3(-1, 1, -1),
float3( 0, -1, -1),
float3( 0, 0, -1),
float3( 0, 1, -1),
float3( 1, -1, -1),
float3( 1, 0, -1),
float3( 1, 1, -1),
float3(-1, -1, 0),
float3(-1, 0, 0),
float3(-1, 1, 0),
float3( 0, -1, 0),
float3( 0, 0, 0),
float3( 0, 1, 0),
float3( 1, -1, 0),
float3( 1, 0, 0),
float3( 1, 1, 0),
float3(-1, -1, 1),
float3(-1, 0, 1),
float3(-1, 1, 1),
float3( 0, -1, 1),
float3( 0, 0, 1),
float3( 0, 1, 1),
float3( 1, -1, 1),
float3( 1, 0, 1),
float3( 1, 1, 1),
};
float Distance(float3 pos)
{
return length(pos);
}
[numthreads(8,8,1)]
void JumpFlooding(uint3 id : SV_DispatchThreadID)
{
float4 nearest = _Input[id];
if (nearest.w < 0.5)
nearest = float4(-10, -10, -10, 0);
int j = 0;
for (int i = 0; i < 27; i++)
{
float3 o = id + offset3D[i] * _Offset;
o %= _Size.x;
o.x += (o.x < 0) ? _Size.x : 0;
o.y += (o.y < 0) ? _Size.x : 0;
o.z += (o.z < 0) ? _Size.x : 0;
float4 n1 = _Output[o];
// Discard invalid samples
if (n1.w < 0.5)
continue;
float3 uv = id / _Size.x;
if (Distance(uv - n1.xyz) < Distance(uv - nearest.xyz))
nearest = n1;
}
_Output[id] = nearest;
}
[numthreads(8, 8, 1)]
void FinalPass(uint3 id : SV_DispatchThreadID)
{
float3 defaultUV = id / _Size.x;
float3 nearestUV = _Input[id].xyz;
float dist = Distance(nearestUV - defaultUV);
_Output[id] = float4(dist.xxx, 1);
}
float2 ProjectOBB(GPUProbeVolumeOBB obb, float3 axis)
{
float pMin = dot(axis, obb.corner);
float pMax = pMin;
for (int x = 0; x < 2; x++)
{
for (int y = 0; y < 2; y++)
{
for (int z = 0; z < 2; z++)
{
float3 vert = obb.corner + obb.X * x + obb.Y * y + obb.Z * z;
float proj = dot(axis, vert);
if (proj < pMin)
pMin = proj;
else if (proj > pMax)
pMax = proj;
}
}
}
return float2(pMin, pMax);
}
bool PointInsideOBB(GPUProbeVolumeOBB obbA, GPUProbeVolumeOBB obbB)
{
float3 axes[] =
{
normalize(obbA.X),
normalize(obbA.Y),
normalize(obbA.Z),
normalize(obbB.X),
normalize(obbB.Y),
normalize(obbB.Z),
};
for (int i = 0; i < 3; i++)
{
float2 obbAProjection = ProjectOBB(obbA, axes[i]);
float2 obbBProjection = ProjectOBB(obbB, axes[i]);
if (obbAProjection.y < obbBProjection.x || obbBProjection.y < obbAProjection.x)
return false;
}
return true;
}
[numthreads(8, 8, 1)]
void VoxelizeProbeVolumeData(uint3 id : SV_DispatchThreadID)
{
float2 subdivisionLimits = float2(0, 0);
float maxGeometryDistance = -1e20;
float maxSubdivLevelInsideVolume = 0;
if (any(id >= uint3(_MaxBrickSize.xyz)))
return;
// Calculate the brick
GPUProbeVolumeOBB brickOBB;
brickOBB.corner = id * _BrickSize + _VolumeWorldOffset.xyz;
brickOBB.X = float3(_BrickSize, 0, 0);
brickOBB.Y = float3(0, _BrickSize, 0);
brickOBB.Z = float3(0, 0, _BrickSize);
for (int i = 0; i < _ProbeVolumeCount; i++)
{
GPUProbeVolumeOBB obb = _ProbeVolumes[i];
if (PointInsideOBB(obb, brickOBB))
{
subdivisionLimits.x = max(subdivisionLimits.x, obb.minControllerSubdivLevel);
subdivisionLimits.y = max(subdivisionLimits.y, obb.maxControllerSubdivLevel);
maxGeometryDistance = max(maxGeometryDistance, obb.geometryDistanceOffset);
maxSubdivLevelInsideVolume = max(maxSubdivLevelInsideVolume, obb.maxSubdivLevelInsideVolume);
}
}
_Output[id] = float4(subdivisionLimits, maxGeometryDistance, maxSubdivLevelInsideVolume);
}
[numthreads(8, 8, 1)]
void Subdivide(uint3 id : SV_DispatchThreadID)
{
if (any(id >= uint3(_MaxBrickSize.xyz)))
return;
// Compute the position at the center of the voxel
float3 position01 = (float3(id) + 0.5) / _MaxBrickSize.xyz;
// Get the local min and max subdivision levels
float4 subdivisionLevelData = _ProbeVolumeData.Load(uint4(id, _SubdivisionLevel));
// Discard bricks that are below the max subdiv level of the probe volume
int maxControllerSubdivLevel = _MaxSubdivisionLevel - subdivisionLevelData.y;
int maxSubdivLevelInsideVolume = subdivisionLevelData.w;
if (_SubdivisionLevel < maxControllerSubdivLevel || _SubdivisionLevel > maxSubdivLevelInsideVolume)
return;
uint3 sdfId = floor(position01 * _SDFSize.xyz);
float dist = _Input[sdfId].r;
float voxelDetectionDistance = rcp(_MaxBrickSize.x);
voxelDetectionDistance *= voxelDetectionDistance;
voxelDetectionDistance = sqrt(voxelDetectionDistance + voxelDetectionDistance + voxelDetectionDistance) / 2.0;
int minControllerSubdivLevel = _MaxSubdivisionLevel - subdivisionLevelData.x - 1;
// Add a small offset to control how the subdivision looks from the probe volume
voxelDetectionDistance += subdivisionLevelData.z * rcp(_MaxBrickSize.x);
if (dist <= voxelDetectionDistance || _SubdivisionLevel > minControllerSubdivLevel)
{
// transform id to world position
float3 positionInCell = _VolumeOffsetInBricks.xyz + (id / _MaxBrickSize.xyz) * _VolumeSizeInBricks.xyz;
_Bricks.Append(positionInCell);
}
}