You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

219 lines
8.5 KiB

#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch
#pragma kernel ComputeVolumetricMaterialRenderingParameters
// #pragma enable_d3d11_debug_symbols
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Builtin/BuiltinData.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/HDRenderPipeline.VolumetricLighting.cs.hlsl"
uint _ViewCount;
// Readonly buffers
StructuredBuffer<OrientedBBox> _VolumeBounds;
StructuredBuffer<LocalVolumetricFogEngineData> _VolumeData;
ByteAddressBuffer _VolumetricVisibleGlobalIndicesBuffer;
RWBuffer<uint> _VolumetricGlobalIndirectArgsBuffer : register( u0 ); // Indirect arguments have to be in a _buffer_, not a structured buffer
RWByteAddressBuffer _VolumetricGlobalIndirectionBuffer : register( u1 );
RWStructuredBuffer<VolumetricMaterialRenderingData> _VolumetricMaterialData;
int DistanceToSlice(float distance)
{
float de = _VBufferRcpSliceCount; // Log-encoded distance between slices
float e1 = EncodeLogarithmicDepthGeneralized(distance, _VBufferDistanceEncodingParams);
e1 -= de;
e1 /= de;
return int(e1 - 0.5);
}
float DepthDistance(float3 position)
{
float cameraDistance = length(position);
float3 viewDirWS = normalize(position);
float3 cameraForward = -UNITY_MATRIX_V[2].xyz;
float depth = cameraDistance * dot(viewDirWS, cameraForward);
return depth;
}
// https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
float DistanceToAABB(float3 p, float3 b)
{
float3 q = abs(p) - b;
return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
}
// Optimized version of https://www.sciencedirect.com/topics/computer-science/oriented-bounding-box
float DistanceDistanceToOBB(float3 p, OrientedBBox obb)
{
float3 offset = p - obb.center;
float3 boxForward = normalize(cross(obb.right, obb.up));
float3 axisAlignedPoint = float3(dot(offset, normalize(obb.right)), dot(offset, normalize(obb.up)), dot(offset, boxForward));
return DistanceToAABB(axisAlignedPoint, float3(obb.extentX, obb.extentY, obb.extentZ));
}
float3 ComputeCubeVertexPositionRWS(OrientedBBox obb, float3 minPositionRWS, float3 vertexMask)
{
float3x3 obbFrame = float3x3(obb.right, obb.up, cross(obb.right, obb.up));
float3 obbExtents = float3(obb.extentX, obb.extentY, obb.extentZ);
return mul((vertexMask * 2 - 1) * obbExtents, (obbFrame)) + obb.center;
}
// vertexMask contains the vertices of the cube in this order
// 6 .+------+ 7
// .' | .'|
// 2 +---+--+'3 |
// | | | |
// | 4.+--+---+ 5
// |.' | .'
// 0 +------+' 1
static const float3 vertexMask[8] = {
float3(0, 0, 0),
float3(1, 0, 0),
float3(0, 1, 0),
float3(1, 1, 0),
float3(0, 0, 1),
float3(1, 0, 1),
float3(0, 1, 1),
float3(1, 1, 1),
};
// Sort cube indices to respect the winding order described in 'A COMPARISON OF GPU BOX-PLANE INTERSECTION ALGORITHMSFOR DIRECT VOLUME RENDERING' section 3
// We do this in a compute so we don't need to sample 3 index tables in the vertex shader
static const uint vertexIndicesMap[8 * 8] = {
0, 1, 4, 2, 3, 5, 6, 7, // front vertex = 1
1, 5, 3, 0, 4, 7, 2, 6, // front vertex = 1
2, 3, 0, 6, 7, 1, 4, 5, // front vertex = 2
3, 7, 1, 2, 6, 5, 0, 4, // front vertex = 3
4, 0, 5, 6, 2, 1, 7, 3, // front vertex = 4
5, 1, 7, 4, 0, 3, 6, 2, // front vertex = 5
6, 2, 4, 7, 3, 0, 5, 1, // front vertex = 6
7, 6, 5, 3, 2, 4, 1, 0, // front vertex = 7
};
void ComputeCubeVerticesOrder(uint volumeIndex)
{
OrientedBBox obb = _VolumeBounds[volumeIndex]; // the OBB is in world space
float3 obbExtents = float3(obb.extentX, obb.extentY, obb.extentZ);
// Find the min and max distance value from vertices
float3 minPositionRWS = obb.center - obbExtents;
float minVertexDistance = -1;
int frontVertexIndex = 0; // Compute the index of the vertex in front of the camera, needed for the slicing algorithm
int i;
for (i = 0; i < 8; i++)
{
// Select the correct starting vertex to sort the cube vertices
float3 vertexNormal = normalize(vertexMask[i] * 2 - 1);
float3 cameraForward = -UNITY_MATRIX_V[2].xyz;
float d = dot(vertexNormal, cameraForward);
if (d > minVertexDistance)
{
minVertexDistance = d;
frontVertexIndex = i;
}
}
// Write the cube vertices in a certain order respecting the winding described in vertexIndicesMap.
uint vertexIndexMapOffset = frontVertexIndex * 8;
for (i = 0; i < 8; i++)
{
float3 vertex = vertexMask[vertexIndicesMap[vertexIndexMapOffset + i]];
_VolumetricMaterialData[volumeIndex].obbVertexPositionWS[i].xyz = ComputeCubeVertexPositionRWS(obb, minPositionRWS, vertex);
}
}
// Generate the compute buffer to dispatch the indirect draw
[numthreads(32, 1, 1)]
void ComputeVolumetricMaterialRenderingParameters(uint3 dispatchThreadID : SV_DispatchThreadID)
{
if (dispatchThreadID.x >= _VolumeCount * _ViewCount)
return;
uint volumeIndex = dispatchThreadID.x % _VolumeCount;
uint globalBufferWriteIndex = _VolumetricVisibleGlobalIndicesBuffer.Load(volumeIndex << 2);
uint viewIndex = dispatchThreadID.x / _VolumeCount;
uint materialDataIndex = volumeIndex + viewIndex * _VolumeCount;
#if defined(UNITY_STEREO_INSTANCING_ENABLED)
unity_StereoEyeIndex = viewIndex;
#endif
// Sort cube vertices for slicing in vertex
#if USE_VERTEX_CUBE_SLICING
ComputeCubeVerticesOrder(volumeIndex);
#endif
OrientedBBox obb = _VolumeBounds[volumeIndex]; // the OBB is in world space
float3 obbExtents = float3(obb.extentX, obb.extentY, obb.extentZ);
float2 minPositionVS = 1;
float2 maxPositionVS = -1;
float cameraDistanceToOBB = DistanceDistanceToOBB(GetCameraPositionWS(), obb);
if (cameraDistanceToOBB <= 0)
{
minPositionVS = -1;
maxPositionVS = 1;
}
// Find the min and max distance value from vertices
float3 minPositionRWS = obb.center - obbExtents;
float maxVertexDepth = 0;
int i;
for (i = 0; i < 8; i++)
{
float3 position = ComputeCubeVertexPositionRWS(obb, minPositionRWS, vertexMask[i]);
float distance = length(position);
maxVertexDepth = max(maxVertexDepth, distance);
if (cameraDistanceToOBB > 0)
{
float4 positionCS = TransformWorldToHClip(position);
// clamp positionCS inside the view in case the point is behind the camera
if (positionCS.w < 0)
{
minPositionVS = -1;
maxPositionVS = 1;
}
else
{
positionCS.xy /= positionCS.w;
minPositionVS = min(positionCS.xy, minPositionVS);
maxPositionVS = max(positionCS.xy, maxPositionVS);
}
}
}
minPositionVS = clamp(minPositionVS, -1, 1);
maxPositionVS = clamp(maxPositionVS, -1, 1);
float vBufferNearPlane = DecodeLogarithmicDepthGeneralized(0, _VBufferDistanceDecodingParams);
float minBoxDistance = max(vBufferNearPlane, cameraDistanceToOBB);
int startSliceIndex = clamp(DistanceToSlice(minBoxDistance), 0, int(_MaxSliceCount));
int stopSliceIndex = DistanceToSlice(maxVertexDepth);
uint sliceCount = clamp(stopSliceIndex - startSliceIndex, 0, int(_MaxSliceCount) - startSliceIndex);
_VolumetricGlobalIndirectArgsBuffer[globalBufferWriteIndex * 5 + 0] = 6; // IndexCountPerInstance
_VolumetricGlobalIndirectArgsBuffer[globalBufferWriteIndex * 5 + 1] = sliceCount * _ViewCount; // InstanceCount
_VolumetricGlobalIndirectArgsBuffer[globalBufferWriteIndex * 5 + 2] = 0; // StartIndexLocation
_VolumetricGlobalIndirectArgsBuffer[globalBufferWriteIndex * 5 + 3] = 0; // BaseVertexLocation
_VolumetricGlobalIndirectArgsBuffer[globalBufferWriteIndex * 5 + 4] = 0; // StartInstanceLocation
// Provide smaller buffer index in the global indirection buffer to sample those buffers in the vertex during the voxelization.
_VolumetricGlobalIndirectionBuffer.Store(globalBufferWriteIndex << 2, volumeIndex);
_VolumetricMaterialData[materialDataIndex].sliceCount = sliceCount;
_VolumetricMaterialData[materialDataIndex].startSliceIndex = startSliceIndex;
_VolumetricMaterialData[materialDataIndex].viewSpaceBounds = float4(minPositionVS, maxPositionVS - minPositionVS);
}