Files
UnrealEngine/Engine/Shaders/Private/RayTracing/RayTracingBuildLightGrid.usf
2025-05-18 13:04:45 +08:00

118 lines
3.5 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#include "/Engine/Shared/LightDefinitions.h"
#include "/Engine/Shared/RayTracingDefinitions.h"
#include "/Engine/Shared/RayTracingTypes.h"
#include "../IntersectionUtils.ush"
uint SceneLightCount;
StructuredBuffer<FRTLightingData> SceneLights;
uint SceneInfiniteLightCount;
float3 SceneLightsTranslatedBoundMin;
float3 SceneLightsTranslatedBoundMax;
RWTexture2DArray<uint> RWLightGrid;
RWBuffer<uint> RWLightGridData;
uint LightGridResolution;
uint LightGridMaxCount;
uint LightGridAxis;
#define RECT_TEST 2 // 0: half-space only
// 1: barndoors (planes only)
// 2: barndoors+aabb culling
#define CONE_TEST 1 // 0: test against cone AABB
// 1: accurate cone/aabb test
[numthreads(THREADGROUPSIZE_X, THREADGROUPSIZE_Y, 1)]
void RayTracingBuildLightGridCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
if (any(DispatchThreadId >= LightGridResolution) || DispatchThreadId.z >= 3)
{
return;
}
// figure out dimension of the 3d grid and the current ID to be filled
uint3 VoxelId = 0, VoxelRes = 1;
int Axis = DispatchThreadId.z;
if (Axis != LightGridAxis && LightGridAxis < 3)
{
RWLightGrid[DispatchThreadId] = 0;
return;
}
switch (Axis)
{
case 0: VoxelId.yz = DispatchThreadId.xy; VoxelRes.yz = LightGridResolution; break;
case 1: VoxelId.xz = DispatchThreadId.xy; VoxelRes.xz = LightGridResolution; break;
case 2: VoxelId.xy = DispatchThreadId.xy; VoxelRes.xy = LightGridResolution; break;
}
// get bounding box of current voxel
float3 Lo = lerp(SceneLightsTranslatedBoundMin, SceneLightsTranslatedBoundMax, float3(VoxelId + 0) / float3(VoxelRes));
float3 Hi = lerp(SceneLightsTranslatedBoundMin, SceneLightsTranslatedBoundMax, float3(VoxelId + 1) / float3(VoxelRes));
uint NumVisible = 0;
uint MaxVisible = LightGridMaxCount;
uint Offset = LightGridMaxCount * (Axis + 3 * (DispatchThreadId.x + LightGridResolution * DispatchThreadId.y));
for (uint LightIndex = SceneInfiniteLightCount; LightIndex < SceneLightCount; LightIndex++)
{
uint LightType = SceneLights[LightIndex].Type;
float3 Center = SceneLights[LightIndex].TranslatedLightPosition;
float Radius = 1.0 / SceneLights[LightIndex].InvRadius;
float3 Normal = -SceneLights[LightIndex].Direction;
if (!AABBOverlapsSphere(Lo, Hi, Center, Radius))
{
// bounds don't overlap there sphere of influence
continue;
}
// reject against normal
if (LightType != LIGHT_TYPE_POINT)
{
if (IsBoxBehindPlane(Lo, Hi, Normal, Center))
{
continue;
}
}
#if RECT_TEST > 0
if (LightType == LIGHT_TYPE_RECT)
{
// check barndoors
const float BarnCos = SceneLights[LightIndex].RectLightBarnCosAngle;
if (BarnCos > 0.035)
{
const float BarnLen = SceneLights[LightIndex].RectLightBarnLength;
const float HalfWidth = SceneLights[LightIndex].SourceRadius;
const float HalfHeight = SceneLights[LightIndex].SourceLength;
if (!AABBOverlapsRectLightFrustum(Lo, Hi, Center, Normal, SceneLights[LightIndex].Tangent, HalfWidth, HalfHeight, Radius, BarnCos, BarnLen, RECT_TEST == 2))
{
continue;
}
}
}
#endif
if (LightType == LIGHT_TYPE_SPOT)
{
const float CosOuter = SceneLights[LightIndex].SpotAngles.x;
if (!AABBOverlapsCurvedCone(Lo, Hi, Center, Normal, CosOuter, Radius, CONE_TEST == 0))
{
continue;
}
}
{
RWLightGridData[Offset + NumVisible] = LightIndex;
NumVisible++;
// TODO: handle overflow better?
if (NumVisible >= MaxVisible)
{
break;
}
}
}
RWLightGrid[DispatchThreadId] = NumVisible;
}