631 lines
20 KiB
HLSL
631 lines
20 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "../Common.ush"
|
|
#include "../DistanceFieldLightingShared.ush"
|
|
#include "../HybridIndirectLighting.ush"
|
|
#include "LumenCardCommon.ush"
|
|
#include "../MeshDistanceFieldCommon.ush"
|
|
#include "../HZB.ush"
|
|
|
|
uint ObjectBoundingGeometryIndexCount;
|
|
float CardTraceEndDistanceFromCamera;
|
|
float MaxMeshSDFInfluenceRadius;
|
|
float MeshSDFRadiusThreshold;
|
|
uint NumCullGridCells;
|
|
|
|
#ifndef THREADGROUP_SIZE
|
|
#define THREADGROUP_SIZE 1
|
|
#endif
|
|
|
|
#define CULL_MESH_TYPE_SDF 0
|
|
#define CULL_MESH_TYPE_HEIGHTFIELD 1
|
|
|
|
#ifndef CULL_MESH_TYPE
|
|
#define CULL_MESH_TYPE CULL_MESH_TYPE_SDF
|
|
#endif
|
|
|
|
struct FObjectData
|
|
{
|
|
uint Index;
|
|
uint Type;
|
|
};
|
|
|
|
FObjectData CreateObjectData(uint ObjectIndex, uint ObjectType)
|
|
{
|
|
FObjectData ObjectData;
|
|
ObjectData.Index = ObjectIndex;
|
|
ObjectData.Type = ObjectType;
|
|
return ObjectData;
|
|
}
|
|
|
|
uint PackObjectData(FObjectData ObjectData)
|
|
{
|
|
uint PackedData;
|
|
PackedData = ObjectData.Index & 0x7FFFFFFF;
|
|
PackedData |= ObjectData.Type << 31;
|
|
return PackedData;
|
|
}
|
|
|
|
FObjectData UnpackObjectData(uint PackedData)
|
|
{
|
|
FObjectData ObjectData;
|
|
ObjectData.Index = PackedData & 0x7FFFFFFF;
|
|
ObjectData.Type = (PackedData >> 31) & 0x1;
|
|
return ObjectData;
|
|
}
|
|
|
|
#if COMPUTESHADER
|
|
groupshared uint NumGroupObjects;
|
|
groupshared uint GroupBaseIndex;
|
|
groupshared uint GroupObjectIndices[THREADGROUP_SIZE];
|
|
#endif
|
|
|
|
RWBuffer<uint> RWObjectIndexBuffer;
|
|
|
|
float LengthSq(float3 X)
|
|
{
|
|
return dot(X, X);
|
|
}
|
|
|
|
#if COMPUTESHADER
|
|
|
|
Buffer<uint> MeshSDFIndexBuffer;
|
|
Buffer<uint> HeightfieldIndexBuffer;
|
|
Buffer<uint> NumCulledMeshSDFObjects;
|
|
Buffer<uint> NumCulledHeightfieldObjects;
|
|
|
|
RWBuffer<uint> RWCombinedObjectIndexBuffer;
|
|
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void CombineObjectIndexBuffersCS(
|
|
uint3 DispatchThreadId : SV_DispatchThreadID
|
|
)
|
|
{
|
|
uint ObjectIndex = DispatchThreadId.x;
|
|
|
|
uint NumObjects = NumCulledMeshSDFObjects[0] + NumCulledHeightfieldObjects[0];
|
|
if (ObjectIndex < NumObjects)
|
|
{
|
|
FObjectData ObjectData;
|
|
if (ObjectIndex < NumCulledHeightfieldObjects[0])
|
|
{
|
|
ObjectData = UnpackObjectData(HeightfieldIndexBuffer[ObjectIndex]);
|
|
}
|
|
else
|
|
{
|
|
uint MeshSDFIndex = ObjectIndex - NumCulledHeightfieldObjects[0];
|
|
ObjectData = UnpackObjectData(MeshSDFIndexBuffer[MeshSDFIndex]);
|
|
}
|
|
|
|
RWCombinedObjectIndexBuffer[ObjectIndex] = PackObjectData(ObjectData);
|
|
}
|
|
}
|
|
|
|
groupshared uint GSNumCulledObjects;
|
|
groupshared uint GSCulledObjectIndex[THREADGROUP_SIZE];
|
|
groupshared uint GSGlobalWriteBaseIndex;
|
|
|
|
uint MaxNumObjects;
|
|
RWBuffer<uint> RWNumCulledObjects;
|
|
RWBuffer<uint> RWCulledObjectIndexBuffer;
|
|
|
|
uint bShouldCull;
|
|
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void CullHeightfieldObjectsForViewCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID,
|
|
uint3 GroupThreadId : SV_GroupThreadID
|
|
)
|
|
{
|
|
if (DispatchThreadId.x == 0)
|
|
{
|
|
RWNumCulledObjects[1] = MaxNumObjects;
|
|
RWObjectIndirectArguments[0] = ObjectBoundingGeometryIndexCount;
|
|
}
|
|
|
|
if (GroupThreadId.x == 0)
|
|
{
|
|
GSNumCulledObjects = 0;
|
|
}
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
bool bShouldKeep = false;
|
|
|
|
uint ObjectIndex = DispatchThreadId.x;
|
|
if (ObjectIndex < MaxNumObjects)
|
|
{
|
|
FLumenHeightfieldData LumenHeightfield = GetLumenHeightfieldData(ObjectIndex);
|
|
if (LumenHeightfield.bValid)
|
|
{
|
|
bShouldKeep = true;
|
|
|
|
if (bShouldCull)
|
|
{
|
|
FLumenMeshCardsData MeshCardsData = GetLumenMeshCardsData(LumenHeightfield.MeshCardsIndex);
|
|
|
|
// Fetch card
|
|
int LocalCardIndex = LUMEN_HEIGHTFIELD_LOCAL_CARD_INDEX;
|
|
FLumenCardData LumenCardData = GetLumenCardData(MeshCardsData.CardOffset + LocalCardIndex);
|
|
|
|
float DistanceFromCameraSq = LengthSq(DFHackToFloat(PrimaryView.WorldCameraOrigin) - LumenCardData.Origin) - LengthSq(LumenCardData.LocalExtent);
|
|
bShouldKeep = DistanceFromCameraSq < CardTraceEndDistanceFromCamera * CardTraceEndDistanceFromCamera;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bShouldKeep)
|
|
{
|
|
uint LocalWriteIndex;
|
|
InterlockedAdd(GSNumCulledObjects, 1u, LocalWriteIndex);
|
|
GSCulledObjectIndex[LocalWriteIndex] = ObjectIndex;
|
|
}
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (GroupThreadId.x == 0)
|
|
{
|
|
uint GlobalWriteBaseIndex;
|
|
InterlockedAdd(RWNumCulledObjects[0], GSNumCulledObjects, GSGlobalWriteBaseIndex);
|
|
InterlockedAdd(RWObjectIndirectArguments[1], GSNumCulledObjects);
|
|
}
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (GroupThreadId.x < GSNumCulledObjects)
|
|
{
|
|
uint GlobalWriteIndex = GSGlobalWriteBaseIndex + GroupThreadId.x;
|
|
FObjectData ObjectData = CreateObjectData(GSCulledObjectIndex[GroupThreadId.x], CULL_MESH_TYPE_HEIGHTFIELD);
|
|
RWCulledObjectIndexBuffer[GlobalWriteIndex] = PackObjectData(ObjectData);
|
|
}
|
|
}
|
|
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void CullMeshSDFObjectsForViewCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID,
|
|
uint3 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
uint ObjectIndex = DispatchThreadId.x;
|
|
|
|
#define USE_FRUSTUM_CULLING 1
|
|
#if USE_FRUSTUM_CULLING
|
|
if (DispatchThreadId.x == 0)
|
|
{
|
|
// RWObjectIndirectArguments is zeroed by a clear before this shader, only need to set things that are non-zero (and are not read by this shader as that would be a race condition)
|
|
// IndexCount, NumInstances, StartIndex, BaseVertexIndex, FirstInstance
|
|
RWObjectIndirectArguments[0] = ObjectBoundingGeometryIndexCount;
|
|
}
|
|
|
|
if (GroupThreadId.x == 0)
|
|
{
|
|
NumGroupObjects = 0;
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (ObjectIndex < NumSceneObjects)
|
|
{
|
|
FDFObjectBounds DFObjectBounds = LoadDFObjectBounds(ObjectIndex);
|
|
const float3 TranslatedCenter = DFFastAddDemote(DFObjectBounds.Center, PrimaryView.PreViewTranslation);
|
|
|
|
float DistanceToViewSq = LengthSq(PrimaryView.TranslatedWorldCameraOrigin - TranslatedCenter);
|
|
|
|
if ((DFObjectBounds.bVisible || DFObjectBounds.bAffectIndirectLightingWhileHidden)
|
|
&& DistanceToViewSq < Square(CardTraceEndDistanceFromCamera + DFObjectBounds.SphereRadius)
|
|
&& (DFObjectBounds.SphereRadius > MeshSDFRadiusThreshold || DFObjectBounds.bEmissiveLightSource)
|
|
&& ViewFrustumIntersectSphere(DFHackToFloat(DFObjectBounds.Center), DFObjectBounds.SphereRadius + MaxMeshSDFInfluenceRadius))
|
|
{
|
|
FDFObjectData DFObjectData = LoadDFObjectData(ObjectIndex);
|
|
|
|
if ((DFObjectData.MinMaxDrawDistance2.x < 0.0001 || DistanceToViewSq > DFObjectData.MinMaxDrawDistance2.x)
|
|
&& (DFObjectData.MinMaxDrawDistance2.y < 0.0001 || DistanceToViewSq < DFObjectData.MinMaxDrawDistance2.y))
|
|
{
|
|
uint DestIndex;
|
|
InterlockedAdd(NumGroupObjects, 1U, DestIndex);
|
|
GroupObjectIndices[DestIndex] = ObjectIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (GroupThreadId.x == 0)
|
|
{
|
|
InterlockedAdd(RWNumCulledObjects[0], NumGroupObjects, GroupBaseIndex);
|
|
InterlockedAdd(RWObjectIndirectArguments[1], NumGroupObjects);
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (GroupThreadId.x < NumGroupObjects)
|
|
{
|
|
uint SourceIndex = GroupObjectIndices[GroupThreadId.x];
|
|
uint DestIndex = GroupBaseIndex + GroupThreadId.x;
|
|
|
|
FObjectData ObjectData = CreateObjectData(SourceIndex, CULL_MESH_TYPE_SDF);
|
|
RWObjectIndexBuffer[DestIndex] = PackObjectData(ObjectData);
|
|
}
|
|
|
|
#else
|
|
|
|
if (DispatchThreadId.x == 0)
|
|
{
|
|
// IndexCount, NumInstances, StartIndex, BaseVertexIndex, FirstInstance
|
|
RWObjectIndirectArguments[0] = ObjectBoundingGeometryIndexCount;
|
|
InterlockedAdd(RWObjectIndirectArguments[1], NumSceneObjects);
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (ObjectIndex < NumSceneObjects)
|
|
{
|
|
uint SourceIndex = ObjectIndex;
|
|
uint DestIndex = ObjectIndex;
|
|
|
|
FObjectData ObjectData = CreateObjectData(SourceIndex, CULL_MESH_TYPE_SDF);
|
|
RWObjectIndexBuffer[DestIndex] = PackObjectData(ObjectData);
|
|
}
|
|
|
|
#endif
|
|
}
|
|
#endif // COMPUTESHADER
|
|
|
|
struct FObjectCullVertexOutput
|
|
{
|
|
nointerpolation float4 PositionAndRadius : TEXCOORD0;
|
|
nointerpolation uint ObjectIndex : TEXCOORD1;
|
|
};
|
|
|
|
float ConservativeRadiusScale;
|
|
Buffer<uint> ObjectIndexBuffer;
|
|
|
|
void MeshSDFObjectCullVS(
|
|
float4 InPosition : ATTRIBUTE0,
|
|
uint IndexInIndexBuffer : SV_InstanceID,
|
|
out FObjectCullVertexOutput Output,
|
|
out float4 OutPosition : SV_POSITION
|
|
)
|
|
{
|
|
FObjectData ObjectData = UnpackObjectData(ObjectIndexBuffer[IndexInIndexBuffer]);
|
|
uint ObjectIndex = ObjectData.Index;
|
|
uint ObjectType = ObjectData.Type;
|
|
float3 ObjectWorldOrigin = 0.0;
|
|
float ObjectWorldRadius = 0.0;
|
|
|
|
#if CULL_MESH_SDF
|
|
if (ObjectType == CULL_MESH_TYPE_SDF)
|
|
{
|
|
FDFObjectBounds DFObjectBounds = LoadDFObjectBounds(ObjectIndex);
|
|
ObjectWorldOrigin = DFHackToFloat(DFObjectBounds.Center);
|
|
ObjectWorldRadius = DFObjectBounds.SphereRadius;
|
|
}
|
|
#endif // CULL_MESH_SDF
|
|
|
|
#if CULL_MESH_HEIGHTFIELD
|
|
if (ObjectType == CULL_MESH_TYPE_HEIGHTFIELD)
|
|
{
|
|
FLumenHeightfieldData LumenHeightfield = GetLumenHeightfieldData(ObjectIndex);
|
|
if (LumenHeightfield.bValid)
|
|
{
|
|
#if 0
|
|
FLumenMeshCardsData MeshCardsData = GetLumenMeshCardsData(LumenHeightfield.MeshCardsIndex);
|
|
FLumenCardData LumenCardData = GetLumenCardData(MeshCardsData.CardOffset + LUMEN_HEIGHTFIELD_LOCAL_CARD_INDEX);
|
|
ObjectWorldOrigin = LumenCardData.Origin;
|
|
ObjectWorldRadius = length(LumenCardData.LocalExtent);
|
|
#else
|
|
ObjectWorldOrigin = DFHackToFloat(LumenHeightfield.BoundsCenter); // LWC_TODO:
|
|
ObjectWorldRadius = length(LumenHeightfield.BoundsExtent);
|
|
#endif
|
|
}
|
|
}
|
|
#endif // CULL_MESH_HEIGHTFIELD
|
|
|
|
float EffectiveRadius = (ObjectWorldRadius + MaxMeshSDFInfluenceRadius) * ConservativeRadiusScale / 0.7f;
|
|
float3 WorldPosition = InPosition.xyz * EffectiveRadius + ObjectWorldOrigin;
|
|
|
|
OutPosition = mul(float4(WorldPosition, 1), DFHackToFloat(PrimaryView.WorldToClip));
|
|
Output.PositionAndRadius.xyz = ObjectWorldOrigin;
|
|
Output.PositionAndRadius.w = ObjectWorldRadius;
|
|
Output.ObjectIndex = PackObjectData(ObjectData);
|
|
}
|
|
|
|
RWBuffer<uint> RWNumGridCulledMeshSDFObjects;
|
|
RWBuffer<uint> RWGridCulledMeshSDFObjectIndicesArray;
|
|
RWBuffer<uint> RWNumGridCulledHeightfieldObjects;
|
|
RWBuffer<uint> RWGridCulledHeightfieldObjectIndicesArray;
|
|
RWBuffer<uint> RWNumCulledObjectsToCompact;
|
|
RWBuffer<uint> RWCulledObjectsToCompactArray;
|
|
|
|
//@todo - put in header for tracing
|
|
Buffer<uint> GridCulledMeshSDFObjectStartOffsetArray;
|
|
Buffer<uint> GridCulledHeightfieldObjectStartOffsetArray;
|
|
#define CULLED_MESH_SDF_DATA_STRIDE 1
|
|
#define CULLED_OBJECT_TO_COMPACT_STRIDE 2
|
|
float3 CardGridZParams;
|
|
uint3 CullGridSize;
|
|
uint CardGridPixelSizeShift;
|
|
|
|
uint ComputeFroxelGridZ(float SceneDepth)
|
|
{
|
|
uint ZSlice = (uint)(max(0, log2(SceneDepth * CardGridZParams.x + CardGridZParams.y) * CardGridZParams.z));
|
|
ZSlice = min(ZSlice, (uint)(CullGridSize.z - 1));
|
|
return ZSlice;
|
|
}
|
|
|
|
float ComputeDepthFromZSlice(float ZSlice)
|
|
{
|
|
#if CULL_TO_FROXEL_GRID
|
|
float SliceDepth = (exp2(ZSlice / CardGridZParams.z) - CardGridZParams.y) / CardGridZParams.x;
|
|
return SliceDepth;
|
|
#else
|
|
float MinTileZ = 0.0f;
|
|
float MaxTileZ = CardTraceEndDistanceFromCamera;
|
|
return lerp(MinTileZ, MaxTileZ, ZSlice);
|
|
#endif
|
|
}
|
|
|
|
float3 ComputeCellWorldPosition(uint3 GridCoordinate, float3 CellOffset)
|
|
{
|
|
float2 VolumeUV = (GridCoordinate.xy + CellOffset.xy) / CullGridSize.xy;
|
|
float2 VolumeNDC = (VolumeUV * 2 - 1) * float2(1, -1);
|
|
|
|
float SceneDepth = ComputeDepthFromZSlice(GridCoordinate.z + CellOffset.z);
|
|
float TileDeviceZ = ConvertToDeviceZ(SceneDepth);
|
|
|
|
float4 CenterPosition = mul(float4(VolumeNDC, TileDeviceZ, 1), View.ClipToTranslatedWorld);
|
|
return CenterPosition.xyz / CenterPosition.w - DFHackToFloat(PrimaryView.PreViewTranslation);
|
|
}
|
|
|
|
uint MaxNumberOfCulledObjects;
|
|
|
|
float HZBMipLevel;
|
|
|
|
void MeshSDFObjectCullPS(
|
|
FObjectCullVertexOutput Input,
|
|
in float4 SVPos : SV_POSITION)
|
|
{
|
|
uint2 PixelPos = SVPos.xy;
|
|
|
|
FObjectData ObjectData = UnpackObjectData(Input.ObjectIndex);
|
|
uint ObjectIndex = ObjectData.Index;
|
|
uint ObjectType = ObjectData.Type;
|
|
|
|
float3 WorldObjectPosition = Input.PositionAndRadius.xyz;
|
|
float3 WorldObjectExtent = 0.0;
|
|
|
|
#if CULL_MESH_SDF
|
|
if (ObjectType == CULL_MESH_TYPE_SDF)
|
|
{
|
|
FDFObjectData DFObjectData = LoadDFObjectData(ObjectIndex);
|
|
WorldObjectExtent = mul(DFObjectData.VolumePositionExtent, abs(((float3x3)DFObjectData.VolumeToWorld.M)));
|
|
}
|
|
#endif // CULL_MESH_SDF
|
|
|
|
#if CULL_MESH_HEIGHTFIELD
|
|
if (ObjectType == CULL_MESH_TYPE_HEIGHTFIELD)
|
|
{
|
|
FLumenHeightfieldData LumenHeightfield = GetLumenHeightfieldData(ObjectIndex);
|
|
if (!LumenHeightfield.bValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
FLumenMeshCardsData MeshCardsData = GetLumenMeshCardsData(LumenHeightfield.MeshCardsIndex);
|
|
FLumenCardData LumenCardData = GetLumenCardData(MeshCardsData.CardOffset + LUMEN_HEIGHTFIELD_LOCAL_CARD_INDEX);
|
|
WorldObjectPosition = LumenCardData.Origin;
|
|
WorldObjectExtent = mul(abs(LumenCardData.WorldToLocalRotation), LumenCardData.LocalExtent);
|
|
#else
|
|
WorldObjectPosition = DFHackToFloat(LumenHeightfield.BoundsCenter); // LWC_TODO:
|
|
WorldObjectExtent = LumenHeightfield.BoundsExtent;
|
|
#endif
|
|
}
|
|
#endif // CULL_MESH_HEIGHTFIELD
|
|
|
|
|
|
float3 ViewSpaceObjectPosition = mul(float4(WorldObjectPosition + DFHackToFloat(PrimaryView.PreViewTranslation), 1), View.TranslatedWorldToView).xyz;
|
|
float3 ViewSpaceObjectInfluenceExtent = mul(WorldObjectExtent, abs((float3x3)View.TranslatedWorldToView)) + MaxMeshSDFInfluenceRadius;
|
|
|
|
float4 FrustumPlanes[6];
|
|
{
|
|
// Setup tile frustum planes
|
|
float2 TileScale = View.ViewSizeAndInvSize.xy * rcp(2 * (1u << CardGridPixelSizeShift));
|
|
float2 TileBias = TileScale - PixelPos - 0.5;
|
|
|
|
float4 C1 = float4(View.ViewToClip._11 * TileScale.x, 0.0f, View.ViewToClip._31 * TileScale.x + TileBias.x, 0.0f);
|
|
float4 C2 = float4(0.0f, -View.ViewToClip._22 * TileScale.y, View.ViewToClip._32 * TileScale.y + TileBias.y, 0.0f);
|
|
float4 C4 = float4(0.0f, 0.0f, 1.0f, 0.0f);
|
|
|
|
FrustumPlanes[0] = C4 - C1;
|
|
FrustumPlanes[1] = C4 + C1;
|
|
FrustumPlanes[2] = C4 - C2;
|
|
FrustumPlanes[3] = C4 + C2;
|
|
|
|
// Normalize tile frustum planes
|
|
for (uint i = 0; i < 4; ++i)
|
|
{
|
|
FrustumPlanes[i] *= rcp(length(FrustumPlanes[i].xyz));
|
|
}
|
|
}
|
|
|
|
uint MinFroxelZ = 0;
|
|
uint MaxFroxelZ = 0;
|
|
|
|
#if CULL_TO_FROXEL_GRID
|
|
float MinDepth = ViewSpaceObjectPosition.z - ViewSpaceObjectInfluenceExtent.z;
|
|
float MaxDepth = ViewSpaceObjectPosition.z + ViewSpaceObjectInfluenceExtent.z;
|
|
|
|
#define CULL_TO_HZB 1
|
|
#if CULL_TO_HZB
|
|
float2 HZBScreenUV = (PixelPos * (1u << CardGridPixelSizeShift) + .5f) * View.ViewSizeAndInvSize.zw * ViewportUVToHZBBufferUV;
|
|
|
|
if (bIsClosestHZBValid)
|
|
{
|
|
MinDepth = max(MinDepth, ConvertFromDeviceZ(ClosestHZBTexture.SampleLevel(GlobalPointClampedSampler, HZBScreenUV, HZBMipLevel).x));
|
|
}
|
|
|
|
MaxDepth = min(MaxDepth, ConvertFromDeviceZ(FurthestHZBTexture.SampleLevel(GlobalPointClampedSampler, HZBScreenUV, HZBMipLevel).x));
|
|
#endif
|
|
|
|
//@todo - make this conservative
|
|
MinFroxelZ = ComputeFroxelGridZ(MinDepth);
|
|
MaxFroxelZ = ComputeFroxelGridZ(MaxDepth);
|
|
#endif
|
|
|
|
for (uint FroxelZ = MinFroxelZ; FroxelZ <= MaxFroxelZ; FroxelZ++)
|
|
{
|
|
uint3 FroxelGridCoordinate = uint3(PixelPos, FroxelZ);
|
|
float MinTileZ = ComputeDepthFromZSlice(FroxelZ);
|
|
float MaxTileZ = ComputeDepthFromZSlice(FroxelZ + 1);
|
|
|
|
FrustumPlanes[4] = float4(0.0f, 0.0f, 1.0f, -MinTileZ);
|
|
FrustumPlanes[5] = float4(0.0f, 0.0f, -1.0f, MaxTileZ);
|
|
|
|
bool bTileIntersectsObject = true;
|
|
|
|
#define INTERSECT_FRUSTUM_WITH_VIEW_AABB 1
|
|
#if INTERSECT_FRUSTUM_WITH_VIEW_AABB
|
|
// Tile frustum plane vs view space MeshSDF AABB
|
|
for (uint i = 0; i < 6; ++i)
|
|
{
|
|
float DistanceFromNodeCenterToPlane = dot(FrustumPlanes[i], float4(ViewSpaceObjectPosition, 1.0f));
|
|
float Radius = dot(ViewSpaceObjectInfluenceExtent, abs(FrustumPlanes[i].xyz));
|
|
bTileIntersectsObject = bTileIntersectsObject && (DistanceFromNodeCenterToPlane >= -Radius);
|
|
}
|
|
#endif
|
|
|
|
#define INTERSECT_FROXEL_WITH_SDF CULL_MESH_SDF
|
|
#if INTERSECT_FROXEL_WITH_SDF
|
|
if (ObjectType == CULL_MESH_TYPE_SDF && bTileIntersectsObject)
|
|
{
|
|
float3 WorldTileCenterPosition = ComputeCellWorldPosition(FroxelGridCoordinate, .5f); // LWC_TODO
|
|
|
|
float3 WorldTileCornerVector = (ComputeCellWorldPosition(FroxelGridCoordinate, float3(1, 1, 1)) - WorldTileCenterPosition);
|
|
float CornerLength = length(WorldTileCornerVector);
|
|
|
|
WorldTileCornerVector = (ComputeCellWorldPosition(FroxelGridCoordinate, float3(0, 0, 1)) - WorldTileCenterPosition);
|
|
CornerLength = max(length(WorldTileCornerVector), CornerLength);
|
|
|
|
float TileBoundingSphereRadius = CornerLength + MaxMeshSDFInfluenceRadius;
|
|
float DistanceToSurface = DistanceToNearestSurfaceForObject(ObjectIndex, DFPromote(WorldTileCenterPosition), TileBoundingSphereRadius);
|
|
bTileIntersectsObject = DistanceToSurface < TileBoundingSphereRadius;
|
|
}
|
|
#endif
|
|
|
|
if (bTileIntersectsObject)
|
|
{
|
|
uint GridIndex = (FroxelGridCoordinate.z * CullGridSize.y + FroxelGridCoordinate.y) * CullGridSize.x + FroxelGridCoordinate.x;
|
|
|
|
if (ObjectType == CULL_MESH_TYPE_SDF)
|
|
{
|
|
InterlockedAdd(RWNumGridCulledMeshSDFObjects[GridIndex], 1);
|
|
}
|
|
else // if (ObjectType == CULL_MESH_TYPE_HEIGHTFIELD)
|
|
{
|
|
InterlockedAdd(RWNumGridCulledHeightfieldObjects[GridIndex], 1);
|
|
}
|
|
|
|
// Write out object index with a corresponding grid index
|
|
uint CulledObjectToCompactIndex;
|
|
InterlockedAdd(RWNumCulledObjectsToCompact[0], 1, CulledObjectToCompactIndex);
|
|
|
|
if (CulledObjectToCompactIndex < MaxNumberOfCulledObjects)
|
|
{
|
|
RWCulledObjectsToCompactArray[CulledObjectToCompactIndex * CULLED_OBJECT_TO_COMPACT_STRIDE + 0] = PackObjectData(ObjectData);
|
|
RWCulledObjectsToCompactArray[CulledObjectToCompactIndex * CULLED_OBJECT_TO_COMPACT_STRIDE + 1] = GridIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Buffer<uint> NumCulledObjectsToCompact;
|
|
Buffer<uint> CulledObjectsToCompactArray;
|
|
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void CompactCulledObjectsCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID,
|
|
uint3 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
uint ObjectToCompactIndex = DispatchThreadId.x;
|
|
|
|
uint NumObjectsToCompact = min(MaxNumberOfCulledObjects, NumCulledObjectsToCompact[0]);
|
|
|
|
if (ObjectToCompactIndex < NumObjectsToCompact)
|
|
{
|
|
FObjectData ObjectData = UnpackObjectData(CulledObjectsToCompactArray[ObjectToCompactIndex * CULLED_OBJECT_TO_COMPACT_STRIDE + 0]);
|
|
uint ObjectIndex = ObjectData.Index;
|
|
uint GridIndex = CulledObjectsToCompactArray[ObjectToCompactIndex * CULLED_OBJECT_TO_COMPACT_STRIDE + 1];
|
|
|
|
#if CULL_MESH_SDF
|
|
if (ObjectData.Type == CULL_MESH_TYPE_SDF)
|
|
{
|
|
uint CulledObjectIndex;
|
|
InterlockedAdd(RWNumGridCulledMeshSDFObjects[GridIndex], 1, CulledObjectIndex);
|
|
|
|
uint CulledObjectDataStart = GridCulledMeshSDFObjectStartOffsetArray[GridIndex];
|
|
|
|
RWGridCulledMeshSDFObjectIndicesArray[(CulledObjectDataStart + CulledObjectIndex) * CULLED_MESH_SDF_DATA_STRIDE + 0] = ObjectIndex;
|
|
}
|
|
#endif // CULL_MESH_SDF
|
|
|
|
#if CULL_MESH_HEIGHTFIELD
|
|
if (ObjectData.Type == CULL_MESH_TYPE_HEIGHTFIELD)
|
|
{
|
|
uint CulledObjectIndex;
|
|
InterlockedAdd(RWNumGridCulledHeightfieldObjects[GridIndex], 1, CulledObjectIndex);
|
|
|
|
uint CulledObjectDataStart = GridCulledHeightfieldObjectStartOffsetArray[GridIndex];
|
|
|
|
RWGridCulledHeightfieldObjectIndicesArray[(CulledObjectDataStart + CulledObjectIndex) * CULLED_MESH_SDF_DATA_STRIDE + 0] = ObjectIndex;
|
|
}
|
|
#endif // CULL_MESH_HEIGHTFIELD
|
|
}
|
|
}
|
|
|
|
|
|
Buffer<uint> NumGridCulledMeshSDFObjects;
|
|
Buffer<uint> NumGridCulledHeightfieldObjects;
|
|
|
|
RWBuffer<uint> RWGridCulledMeshSDFObjectStartOffsetArray;
|
|
RWBuffer<uint> RWCulledMeshSDFObjectAllocator;
|
|
RWBuffer<uint> RWGridCulledHeightfieldObjectStartOffsetArray;
|
|
RWBuffer<uint> RWCulledHeightfieldObjectAllocator;
|
|
RWBuffer<uint> RWCompactCulledObjectsIndirectArguments;
|
|
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void ComputeCulledObjectsStartOffsetCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID,
|
|
uint3 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
uint CellIndex = DispatchThreadId.x;
|
|
|
|
if (DispatchThreadId.x == 0)
|
|
{
|
|
RWCompactCulledObjectsIndirectArguments[0] = DivideAndRoundUp64(NumCulledObjectsToCompact[0]);
|
|
RWCompactCulledObjectsIndirectArguments[1] = 1;
|
|
RWCompactCulledObjectsIndirectArguments[2] = 1;
|
|
}
|
|
|
|
if (CellIndex < NumCullGridCells)
|
|
{
|
|
// Mesh SDF
|
|
{
|
|
uint NumIntersectingObjects = NumGridCulledMeshSDFObjects[CellIndex];
|
|
|
|
uint StartOffset;
|
|
InterlockedAdd(RWCulledMeshSDFObjectAllocator[0], NumIntersectingObjects, StartOffset);
|
|
RWGridCulledMeshSDFObjectStartOffsetArray[CellIndex] = StartOffset;
|
|
}
|
|
|
|
// Heightfield
|
|
{
|
|
uint NumIntersectingObjects = NumGridCulledHeightfieldObjects[CellIndex];
|
|
|
|
uint StartOffset;
|
|
InterlockedAdd(RWCulledHeightfieldObjectAllocator[0], NumIntersectingObjects, StartOffset);
|
|
RWGridCulledHeightfieldObjectStartOffsetArray[CellIndex] = StartOffset;
|
|
}
|
|
}
|
|
}
|