浏览代码

Implement the clustered density volume pass

/main
Evgenii Golubev 6 年前
当前提交
598b6ad6
共有 4 个文件被更改,包括 197 次插入88 次删除
  1. 10
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/LightLoop.cs
  2. 10
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/LightLoopDef.hlsl
  3. 223
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumeVoxelization.compute
  4. 42
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.compute

10
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/LightLoop.cs


// Inject density volumes into the clustered data structure for efficient look up.
m_densityVolumeCount = densityVolumes.bounds != null ? densityVolumes.bounds.Count : 0;
Matrix4x4 worldToViewCR = worldToView;
if (ShaderConfig.s_CameraRelativeRendering != 0)
{
// The OBBs are camera-relative, the matrix is not. Fix it.
worldToViewCR.SetColumn(3, new Vector4(0, 0, 0, 1));
}
AddBoxVolumeDataAndBound(densityVolumes.bounds[i], LightCategory.DensityVolume, featureFlags, worldToView);
AddBoxVolumeDataAndBound(densityVolumes.bounds[i], LightCategory.DensityVolume, featureFlags, worldToViewCR);
}
}

10
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/LightLoopDef.hlsl


return 1;
}
uint FetchIndex(uint tileOffset, uint lightIndex)
{
return tileOffset + lightIndex;
}
#ifdef LIGHTLOOP_TILE_PASS
#else
int j = start + i;
#endif
return _LightDatas[j];
}

223
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumeVoxelization.compute


// Implementation
//--------------------------------------------------------------------------------------------------
void FillVolumetricDensityBuffer(uint2 voxelCoord, float3 rayOriginWS, float3 rayUnDirWS,
void FillVolumetricDensityBuffer(PositionInputs posInput, float3 rayOriginWS, float3 rayUnDirWS,
float3 voxelAxisRight, float3 voxelAxisUp, float3 voxelAxisForward)
{
float n = _VBufferDepthDecodingParams.x + _VBufferDepthDecodingParams.z;

#ifdef USE_CLUSTERED_LIGHTLIST
// Our voxel is not necessarily completely inside a single light cluster (along Z).
// Note that Z-binning can solve this problem, as we can iterate over all Z-bins
// to compute min/max light indices, and then use this range for the entire slice.
uint volumeStarts[2], volumeCounts[2];
GetCountAndStartCluster(posInput.tileCoord, GetLightClusterIndex(posInput.tileCoord, z0),
LIGHTCATEGORY_DENSITY_VOLUME, volumeStarts[0], volumeCounts[0]);
#endif // USE_CLUSTERED_LIGHTLIST
#if defined(SHADER_API_METAL)
[fastopt]
for (uint slice = 0; slice < VBUFFER_SLICE_COUNT; slice++)

for (uint slice = 0; slice < sliceCountHack; slice++)
#endif
{
uint3 voxelCoord = uint3(posInput.positionSS, slice);
float e1 = slice * de + de; // (slice + 1) / sliceCount
#if defined(SHADER_API_METAL)
// Warning: this compiles, but it's nonsense. Use DecodeLogarithmicDepthGeneralized().

float3 voxelScattering = _GlobalScattering;
float voxelExtinction = _GlobalExtinction;
for (uint i = 0; i < _NumVisibleDensityVolumes; i++)
#ifdef USE_CLUSTERED_LIGHTLIST
GetCountAndStartCluster(posInput.tileCoord, GetLightClusterIndex(posInput.tileCoord, z1),
LIGHTCATEGORY_DENSITY_VOLUME, volumeStarts[1], volumeCounts[1]);
// We now iterate over all density volumes within the two clusters along Z.
// We need to skip duplicates, but it's not too difficult since volumes are sorted by index.
uint i = 0, j = 0;
if (i < volumeCounts[0] || j < volumeCounts[1])
OrientedBBox obb = _VolumeBounds[i];
// At least one of the clusters is non-empty.
uint volumeIndices[2];
float3x3 obbFrame = float3x3(obb.right, obb.up, cross(obb.up, obb.right));
float3 obbExtents = float3(obb.extentX, obb.extentY, obb.extentZ);
// Fetch two initial indices from both clusters.
if (i < volumeCounts[0])
{
volumeIndices[0] = FetchIndex(volumeStarts[0], i);
}
else
{
volumeIndices[0] = UINT_MAX;
}
// Express the voxel center in the local coordinate system of the box.
float3 voxelCenterBS = mul(voxelCenterWS - obb.center, transpose(obbFrame));
float3 voxelCenterUV = voxelCenterBS / obbExtents;
if (j < volumeCounts[1])
{
volumeIndices[1] = FetchIndex(volumeStarts[1], j);
}
else
{
volumeIndices[1] = UINT_MAX;
}
#if SOFT_VOXELIZATION
// We need to determine which is the face closest to 'voxelCenterBS'.
float minFaceDist = abs(obbExtents.x - abs(voxelCenterBS.x));
do
{
// Process volumes in order.
uint volumeIndex = min(volumeIndices[0], volumeIndices[1]);
// TODO: use v_cubeid_f32.
uint axisIndex; float faceDist;
#else // USE_CLUSTERED_LIGHTLIST
{
for (uint i = 0; i < _NumVisibleDensityVolumes; i++)
{
uint volumeIndex = i;
faceDist = abs(obbExtents.y - abs(voxelCenterBS.y));
axisIndex = (faceDist < minFaceDist) ? 1 : 0;
minFaceDist = min(faceDist, minFaceDist);
#endif // USE_CLUSTERED_LIGHTLIST
faceDist = abs(obbExtents.z - abs(voxelCenterBS.z));
axisIndex = (faceDist < minFaceDist) ? 2 : axisIndex;
OrientedBBox obb = _VolumeBounds[volumeIndex];
float3 N = float3(axisIndex == 0 ? 1 : 0, axisIndex == 1 ? 1 : 0, axisIndex == 2 ? 1 : 0);
float3x3 obbFrame = float3x3(obb.right, obb.up, cross(obb.up, obb.right));
float3 obbExtents = float3(obb.extentX, obb.extentY, obb.extentZ);
// We have determined the normal of the closest face.
// We now have to construct the diagonal of the voxel with the longest extent along this normal.
float3 minDiagPointBS, maxDiagPointBS;
// Express the voxel center in the local coordinate system of the box.
float3 voxelCenterBS = mul(voxelCenterWS - obb.center, transpose(obbFrame));
float3 voxelCenterUV = voxelCenterBS / obbExtents;
float3 voxelAxisRightBS = mul(voxelAxisRight, transpose(obbFrame));
float3 voxelAxisUpBS = mul(voxelAxisUp, transpose(obbFrame));
float3 voxelAxisForwardBS = mul(voxelAxisForward, transpose(obbFrame));
#if SOFT_VOXELIZATION
// We need to determine which is the face closest to 'voxelCenterBS'.
float minFaceDist = abs(obbExtents.x - abs(voxelCenterBS.x));
// Start at the center of the voxel.
minDiagPointBS = maxDiagPointBS = voxelCenterBS;
// TODO: use v_cubeid_f32.
uint axisIndex; float faceDist;
faceDist = abs(obbExtents.y - abs(voxelCenterBS.y));
axisIndex = (faceDist < minFaceDist) ? 1 : 0;
minFaceDist = min(faceDist, minFaceDist);
faceDist = abs(obbExtents.z - abs(voxelCenterBS.z));
axisIndex = (faceDist < minFaceDist) ? 2 : axisIndex;
float3 N = float3(axisIndex == 0 ? 1 : 0, axisIndex == 1 ? 1 : 0, axisIndex == 2 ? 1 : 0);
// We have determined the normal of the closest face.
// We now have to construct the diagonal of the voxel with the longest extent along this normal.
float3 minDiagPointBS, maxDiagPointBS;
float3 voxelAxisRightBS = mul(voxelAxisRight, transpose(obbFrame));
float3 voxelAxisUpBS = mul(voxelAxisUp, transpose(obbFrame));
float3 voxelAxisForwardBS = mul(voxelAxisForward, transpose(obbFrame));
// Start at the center of the voxel.
minDiagPointBS = maxDiagPointBS = voxelCenterBS;
bool normalFwd = dot(voxelAxisForwardBS, N) >= 0;
float mulForward = normalFwd ? halfDZ : -halfDZ;
float mulMin = normalFwd ? z0 : z1;
float mulMax = normalFwd ? z1 : z0;
bool normalFwd = dot(voxelAxisForwardBS, N) >= 0;
float mulForward = normalFwd ? halfDZ : -halfDZ;
float mulMin = normalFwd ? z0 : z1;
float mulMax = normalFwd ? z1 : z0;
minDiagPointBS -= mulForward * voxelAxisForwardBS;
maxDiagPointBS += mulForward * voxelAxisForwardBS;
minDiagPointBS -= mulForward * voxelAxisForwardBS;
maxDiagPointBS += mulForward * voxelAxisForwardBS;
float mulUp = dot(voxelAxisUpBS, N) >= 0 ? 1 : -1;
float mulUp = dot(voxelAxisUpBS, N) >= 0 ? 1 : -1;
minDiagPointBS -= (mulMin * mulUp) * voxelAxisUpBS;
maxDiagPointBS += (mulMax * mulUp) * voxelAxisUpBS;
minDiagPointBS -= (mulMin * mulUp) * voxelAxisUpBS;
maxDiagPointBS += (mulMax * mulUp) * voxelAxisUpBS;
float mulRight = dot(voxelAxisRightBS, N) >= 0 ? 1 : -1;
float mulRight = dot(voxelAxisRightBS, N) >= 0 ? 1 : -1;
minDiagPointBS -= (mulMin * mulRight) * voxelAxisRightBS;
maxDiagPointBS += (mulMax * mulRight) * voxelAxisRightBS;
minDiagPointBS -= (mulMin * mulRight) * voxelAxisRightBS;
maxDiagPointBS += (mulMax * mulRight) * voxelAxisRightBS;
// We want to determine the fractional overlap of the diagonal and the box.
float3 diagOriginBS = minDiagPointBS;
float3 diagUnDirBS = maxDiagPointBS - minDiagPointBS;
// We want to determine the fractional overlap of the diagonal and the box.
float3 diagOriginBS = minDiagPointBS;
float3 diagUnDirBS = maxDiagPointBS - minDiagPointBS;
float tEntr, tExit;
float tEntr, tExit;
IntersectRayAABB(diagOriginBS, diagUnDirBS,
-obbExtents, obbExtents,
0, 1,
tEntr, tExit);
IntersectRayAABB(diagOriginBS, diagUnDirBS,
-obbExtents, obbExtents,
0, 1,
tEntr, tExit);
float overlapFraction = tExit - tEntr;
float overlapFraction = tExit - tEntr;
#else // SOFT_VOXELIZATION
#else // SOFT_VOXELIZATION
bool overlap = abs(voxelCenterUV.x) <= 1 &&
abs(voxelCenterUV.y) <= 1 &&
abs(voxelCenterUV.z) <= 1;
bool overlap = abs(voxelCenterUV.x) <= 1 &&
abs(voxelCenterUV.y) <= 1 &&
abs(voxelCenterUV.z) <= 1;
float overlapFraction = overlap ? 1 : 0;
float overlapFraction = overlap ? 1 : 0;
#endif // SOFT_VOXELIZATION
#endif // SOFT_VOXELIZATION
if (overlapFraction > 0)
{
// There is an overlap. Sample the 3D texture, or load the constant value.
voxelScattering += overlapFraction * _VolumeProperties[volumeIndex].scattering;
voxelExtinction += overlapFraction * _VolumeProperties[volumeIndex].extinction;
}
if (overlapFraction > 0)
{
// There is an overlap. Sample the 3D texture, or load the constant value.
voxelScattering += overlapFraction * _VolumeProperties[i].scattering;
voxelExtinction += overlapFraction * _VolumeProperties[i].extinction;
#ifndef USE_CLUSTERED_LIGHTLIST
#else // USE_CLUSTERED_LIGHTLIST
_VBufferDensity[uint3(voxelCoord, slice)] = float4(voxelScattering, voxelExtinction);
// Advance to the next volume in one (or both at the same time) clusters.
if (volumeIndex == volumeIndices[0])
{
i++;
if (i < volumeCounts[0])
{
volumeIndices[0] = FetchIndex(volumeStarts[0], i);
}
else
{
volumeIndices[0] = UINT_MAX;
}
}
if (volumeIndex == volumeIndices[1])
{
j++;
if (j < volumeCounts[1])
{
volumeIndices[1] = FetchIndex(volumeStarts[1], j);
}
else
{
volumeIndices[1] = UINT_MAX;
}
}
} while (i < volumeCounts[0] || j < volumeCounts[1]);
}
// We don't need to carry over the cluster index, only the start and the count.
volumeStarts[0] = volumeStarts[1];
volumeCounts[0] = volumeCounts[1];
#endif // USE_CLUSTERED_LIGHTLIST
_VBufferDensity[voxelCoord] = float4(voxelScattering, voxelExtinction);
z0 = z1;
}

float3 leftDirWS = mul(-float3(leftCoord, 1), (float3x3)_VBufferCoordToViewDirWS);
float3 upDirWS = mul(-float3(upCoord, 1), (float3x3)_VBufferCoordToViewDirWS);
// Compute the axes of the voxel.
// Compute the axes of the voxel. These are not normalized, but rather computed to scale with Z.
FillVolumetricDensityBuffer(voxelCoord, GetCurrentViewPosition(), centerDirWS,
PositionInputs posInput = GetPositionInput(voxelCoord, _VBufferResolution.zw, tileCoord);
FillVolumetricDensityBuffer(posInput, GetCurrentViewPosition(), centerDirWS,
voxelAxisRight, voxelAxisUp, voxelAxisForward);
}

42
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.compute


// Multiplication by the scattering coefficient and the phase function is performed outside.
VoxelLighting EvaluateVoxelLighting(LightLoopContext context, uint featureFlags, PositionInputs posInput, float3 centerWS,
DualRay ray, float t0, float t1, float dt, float rndVal, float extinction, float asymmetry
#ifdef LIGHTLOOP_TILE_PASS
#ifdef USE_CLUSTERED_LIGHTLIST
, uint clusterIndices[2], float clusterDepths[2])
#else
)

return lighting;
#endif
#ifdef LIGHTLOOP_TILE_PASS
#ifdef USE_CLUSTERED_LIGHTLIST
// Loop over 1 or 2 light clusters.
int cluster = 0;
do

{
tMax = min(t1, ray.strataDirInvViewZ * clusterDepths[1]);
}
#else
#else // USE_CLUSTERED_LIGHTLIST
#endif // LIGHTLOOP_TILE_PASS
#endif // USE_CLUSTERED_LIGHTLIST
#ifdef LIGHTLOOP_TILE_PASS
#ifdef USE_CLUSTERED_LIGHTLIST
#else
#else // USE_CLUSTERED_LIGHTLIST
#endif // LIGHTLOOP_TILE_PASS
#endif // USE_CLUSTERED_LIGHTLIST
uint i = 0, last = lightCount - 1;
int i = 0, last = lightCount - 1;
// Box lights require special handling (see the next while loop).
while (i <= last && light.lightType != GPULIGHTTYPE_PROJECTOR_BOX)

}
}
}
#ifdef LIGHTLOOP_TILE_PASS
#ifdef USE_CLUSTERED_LIGHTLIST
#endif // LIGHTLOOP_TILE_PASS
#endif // USE_CLUSTERED_LIGHTLIST
return lighting;
}

float3 totalRadiance = 0;
float opticalDepth = 0;
#ifdef LIGHTLOOP_TILE_PASS
// Our voxel is not necessarily completely inside a single light cluster.
#ifdef USE_CLUSTERED_LIGHTLIST
// Our voxel is not necessarily completely inside a single light cluster (along Z).
// Note that Z-binning can solve this problem, as we can iterate over all Z-bins
// to compute min/max light indices, and then use this range for the entire slice.
uint clusterIndices[2];

#endif // LIGHTLOOP_TILE_PASS
#endif // USE_CLUSTERED_LIGHTLIST
#if defined(SHADER_API_METAL)
[fastopt]

float t1 = ray.strataDirInvViewZ * z1; // Convert view space Z to distance along the stratified ray
float dt = t1 - t0;
#ifdef LIGHTLOOP_TILE_PASS
#ifdef USE_CLUSTERED_LIGHTLIST
clusterIndices[1] = GetLightClusterIndex(posInput.tileCoord, z1);
clusterDepths[1] = GetLightClusterMinLinearDepth(posInput.tileCoord, clusterIndices[1]);
#endif

VoxelLighting lighting = EvaluateVoxelLighting(context, featureFlags, posInput, centerWS,
ray, t0, t1, dt, rndVal, extinction, asymmetry
#ifdef LIGHTLOOP_TILE_PASS
#ifdef USE_CLUSTERED_LIGHTLIST
, clusterIndices, clusterDepths);
#else
);

// Use max() to prevent division by 0.
float3 phaseCurrFrame = lighting.radianceComplete * rcp(max(lighting.radianceNoPhase, FLT_MIN));
blendedRadiance *= phaseCurrFrame;
#endif // SUPPORT_ASYMMETRY
#endif
#else // ENABLE_REPROJECTION
#else // ENABLE_REPROJECTION
#else // SUPPORT_ASYMMETRY
#else
#endif // SUPPORT_ASYMMETRY
#endif
#endif // ENABLE_REPROJECTION

t0 = t1;
#ifdef LIGHTLOOP_TILE_PASS
#ifdef USE_CLUSTERED_LIGHTLIST
#endif // LIGHTLOOP_TILE_PASS
#endif
}
}

正在加载...
取消
保存