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

493 lines
21 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ClusteredDeferredShadingPixelShader - applies all local lights in the
LightGrid in a full-screen pass, not directional ones as they are done
in the traditional deferred passes.
=============================================================================*/
// Following the lead of Tiled Deferred
#define SUPPORT_CONTACT_SHADOWS 0
#define USE_IES_PROFILE 1
#define NON_DIRECTIONAL_DIRECT_LIGHTING 0
#define SUBSTRATE_SSS_TRANSMISSION USE_TRANSMISSION
// Used in RectLight.ush, and requires a per-light texture which we cannot support at present.
// If the textures are atlased or something like that in the future it should be ok to turn on.
#define USE_SOURCE_TEXTURE 0
#if SUBSTRATE_ENABLED
#if SUBSTRATE_TILETYPE == 0
#define SUBSTRATE_FASTPATH 1
#elif SUBSTRATE_TILETYPE == 1
#define SUBSTRATE_SINGLEPATH 1
#elif SUBSTRATE_TILETYPE == 2
// COMPLEX PATH
#elif SUBSTRATE_TILETYPE == 3
// COMPLEX PATH
#define SUBSTRATE_COMPLEXSPECIALPATH 1
#else
#error Substrate tile type non-implemented
#endif
#endif
#include "HairStrands/HairStrandsVisibilityCommonStruct.ush"
#include "Common.ush"
#include "Substrate/Substrate.ush"
#include "DeferredShadingCommon.ush"
#include "BRDF.ush"
#include "ShadingModels.ush"
#include "LightGridCommon.ush"
#include "DeferredLightingCommon.ush"
#include "LightData.ush"
#include "VirtualShadowMaps/VirtualShadowMapTransmissionCommon.ush"
#include "VirtualShadowMaps/VirtualShadowMapMaskBitsCommon.ush"
#include "Substrate/SubstrateEvaluation.ush"
#include "Substrate/SubstrateDeferredLighting.ush"
#if USE_HAIR_LIGHTING == 1
#include "HairStrands/HairStrandsCommon.ush"
#include "HairStrands/HairStrandsVisibilityCommon.ush"
#include "HairStrands/HairStrandsVisibilityUtils.ush"
#include "HairStrands/HairStrandsDeepTransmittanceCommon.ush"
#include "HairStrands/HairStrandsDeepTransmittanceDualScattering.ush"
#endif // USE_HAIR_LIGHTING
// Causes the shader to run one pass over the light list per shading model, which cuts down peak register pressure.
// Moves some overhead out of the inner loop (testing for shading model) which ought to be a good thing also.
#if SUBSTRATE_ENABLED
#define USE_PASS_PER_SHADING_MODEL 0
#else
// Only enable on non-FXC compilers since FXC takes ages to compile all the shader model passes.
#define USE_PASS_PER_SHADING_MODEL (COMPILER_DXC == 1 || COMPILER_PSSL == 1 || COMPILER_METAL == 1)
#endif
// Temporary work around for shader compilation issue
#define SUPPORT_SUBSTRATE_LIGHTING (COMPILER_DXC == 1 || COMPILER_PSSL == 1 || COMPILER_METAL == 1)
// Enable cluster debug visualization
#define GRID_DEBUG_ENABLE 0
Texture2D<uint4> ShadowMaskBits;
#if USE_HAIR_LIGHTING
uint HairTransmittanceBufferMaxCount;
Buffer<uint> HairTransmittanceBuffer;
#endif
/**
* Debug function for visualizing clusters
*/
bool GetClusterDebugColor(float2 LocalPosition, float SceneDepth, FCulledLightsGridHeader GridData, inout float4 OutColor)
{
OutColor = 0.0f;
#if GRID_DEBUG_ENABLE
uint ZSlice = (uint)(max(0, log2(SceneDepth * GridData.LightGridZParams.x + GridData.LightGridZParams.y) * GridData.LightGridZParams.z));
ZSlice = min(ZSlice, (uint)(GridData.CulledGridSize.z - 1));
uint3 GridCoordinate = uint3(uint2(LocalPosition.x, LocalPosition.y) >> GridData.LightGridPixelSizeShift, ZSlice);
if (ZSlice % 2)
{
OutColor = float(ZSlice) / float(GridData.CulledGridSize.z - 1);
}
else
{
OutColor = 1.0f - float(ZSlice) / float(GridData.CulledGridSize.z - 1);
}
if (GridCoordinate.x % 2)
{
OutColor.xyz = 1.0f - OutColor.xyz;
}
if (GridCoordinate.y % 2)
{
OutColor.xyz = 1.0f - OutColor.xyz;
}
OutColor.w = 0.0f;
return true;
#else
return false;
#endif
}
/**
* Adds local lighting using the light grid, does not apply directional lights, as they are done elsewhere.
* Does not support dynamic shadows, as these require the per-light shadow mask.
*/
float4 GetLightGridLocalLighting(
#if SUBTRATE_GBUFFER_FORMAT==1 && USE_HAIR_LIGHTING == 0
FSubstrateAddressing SubstrateAddressing,
FSubstratePixelHeader SubstratePixelHeader,
inout float3 OutOpaqueRoughRefractionSceneColor,
inout float3 OutSubSurfaceSceneColor,
#else
FScreenSpaceData ScreenSpaceData,
#endif
const FCulledLightsGridHeader InLightGridHeader,
float3 TranslatedWorldPosition,
float3 CameraVector,
float2 ScreenUV,
float SceneDepth,
uint EyeIndex,
float Dither,
uint SampleIndex=0,
uint TotalSampleCount=0,
uint2 PixelCoord=0,
uint HairLightChannelMask=0)
{
float4 DirectLighting = 0;
// Limit max to ForwardLightStruct.NumLocalLights.
// This prevents GPU hangs when the PS tries to read from uninitialized NumCulledLightsGrid buffer
const uint NumLightsInGridCell = min(InLightGridHeader.NumLights, GetMaxLightsPerCell());
int2 PixelPos = int2( ScreenUV.xy * View.BufferSizeAndInvSize.xy );
uint4 ShadowMask = ShadowMaskBits[ PixelPos ];
#if USE_HAIR_LIGHTING
uint LightChannelMask = HairLightChannelMask;
#else
uint LightChannelMask = GetSceneLightingChannel(PixelPos);
#endif
LOOP
for (uint GridLightListIndex = 0; GridLightListIndex < NumLightsInGridCell; GridLightListIndex++)
{
const FLocalLightData LocalLight = GetLocalLightDataFromGrid(InLightGridHeader.DataStartIndex + GridLightListIndex, EyeIndex);
// The lights are sorted such that all that support clustered deferred are at the beginning, there might be others
// (e.g., lights with dynamic shadows) so we break out when the condition fails.
if (!UnpackIsClusteredDeferredSupported(LocalLight))
{
break;
}
if (!IsLightVisible(LocalLight, TranslatedWorldPosition))
{
continue;
}
if ((LightChannelMask & UnpackLightingChannelMask(LocalLight)) == 0)
{
continue;
}
FDeferredLightData LightData = ConvertToDeferredLight(LocalLight);
LightData.ShadowedBits = 1;
LightData.bRectLight = LightData.bRectLight && USE_RECT_LIGHT;
#if USE_IES_PROFILE
LightData.Color *= ComputeLightProfileMultiplier(TranslatedWorldPosition, LightData.TranslatedWorldPosition, -LightData.Direction, LightData.Tangent, LightData.IESAtlasIndex);
#endif
float4 LightAttenuation = float4(1, 1, 1, 1);
// Find packed VSM projection result if present
// NOTE: We have to do the search as in the deferred pass again currently as the shadow masks
// are packed according to a prune light list that contains only shadow-casting lights.
uint VSMGridLightListIndex = GridLightListIndex;
if (LocalLight.Internal.VirtualShadowMapId != INDEX_NONE)
{
LightAttenuation = GetVirtualShadowMapMaskForLight(
ShadowMask,
uint2(PixelPos),
SceneDepth,
LocalLight.Internal.VirtualShadowMapId,
TranslatedWorldPosition,
VSMGridLightListIndex).xxxx;
}
#if USE_HAIR_LIGHTING
if (LightAttenuation.x > 0)
{
const float3 L = normalize(LightData.TranslatedWorldPosition - TranslatedWorldPosition);
const float3 V = normalize(-CameraVector);
// Fetch precomputed transmittance
// * Transmittance data are only computed for shadowed lights with VSM
// * Transmittance mask index is computed using VSM light index remapping. This needs to match HairStrandsDeepTransmittanceMask.usf
FHairTransmittanceMask TransmittanceMask = InitHairTransmittanceMask();
if (LocalLight.Internal.VirtualShadowMapId != INDEX_NONE && VSMGridLightListIndex < GetPackedShadowMaskMaxLightCount())
{
const uint TransmittanceSampleIndex = SampleIndex + VSMGridLightListIndex * TotalSampleCount;
const uint TransmittanceSampleMaxCount = TotalSampleCount * GetPackedShadowMaskMaxLightCount();
if (TransmittanceSampleIndex < TransmittanceSampleMaxCount)
{
TransmittanceMask = UnpackTransmittanceMask(HairTransmittanceBuffer[TransmittanceSampleIndex]);
}
}
// Apply dual scattering
LightData.HairTransmittance = GetHairTransmittance(
V,
L,
ScreenSpaceData.GBuffer,
TransmittanceMask,
View.HairScatteringLUTTexture,
HairScatteringLUTSampler,
View.HairComponents);
// This shouldn't be needed any more as the virtual shadow map should offer enough spatial precision
LightAttenuation = min(LightAttenuation, LightData.HairTransmittance.OpaqueVisibility.xxxx);
}
#endif
#if SUBTRATE_GBUFFER_FORMAT==1 && USE_HAIR_LIGHTING == 0
FSubstrateShadowTermInputParameters SubstrateShadowTermInputParameters = GetInitialisedSubstrateShadowTermInputParameters();
SubstrateShadowTermInputParameters.bEvaluateShadowTerm = true;
SubstrateShadowTermInputParameters.SceneDepth = CalcSceneDepth(ScreenUV);
SubstrateShadowTermInputParameters.PrecomputedShadowFactors = SubstrateReadPrecomputedShadowFactors(SubstratePixelHeader, PixelPos, SceneTexturesStruct.GBufferETexture);
SubstrateShadowTermInputParameters.TranslatedWorldPosition = TranslatedWorldPosition;
SubstrateShadowTermInputParameters.LightAttenuation = LightAttenuation;
SubstrateShadowTermInputParameters.Dither = Dither;
const float3 V = -CameraVector;
float3 ToLight, L;
const float LightMask = GetLocalLightAttenuation(TranslatedWorldPosition, LightData, ToLight, L);
FSubstrateDeferredLighting SubstrateLighting = (FSubstrateDeferredLighting)0;
#if SUPPORT_SUBSTRATE_LIGHTING
SubstrateLighting = SubstrateDeferredLighting(
LightData,
V,
L,
ToLight,
LightMask,
SubstrateShadowTermInputParameters,
Substrate.MaterialTextureArray,
SubstrateAddressing,
SubstratePixelHeader);
#endif
DirectLighting += SubstrateLighting.SceneColor;
#if SUBSTRATE_OPAQUE_ROUGH_REFRACTION_ENABLED
OutOpaqueRoughRefractionSceneColor += SubstrateLighting.OpaqueRoughRefractionSceneColor;
OutSubSurfaceSceneColor += SubstrateLighting.SubSurfaceSceneColor;
#endif
#elif 1
// NOTE: uses AO data like tiled deferred (as opposed to forward path)
// TODO: Passing in 0,0 as SVPos, what is meaning of this? Only used if 'REFERENCE_QUALITY' is on.
float SurfaceShadow = 1.0f;
DirectLighting += GetDynamicLighting(TranslatedWorldPosition, CameraVector, ScreenSpaceData.GBuffer, ScreenSpaceData.AmbientOcclusion, LightData,LightAttenuation, Dither, uint2(0, 0), SurfaceShadow);
#else // 1
FSimpleDeferredLightData SimpleLightData = (FSimpleDeferredLightData)0;
SimpleLightData.TranslatedWorldPosition = LightData.TranslatedWorldPosition;
SimpleLightData.InvRadius = LightData.InvRadius;
SimpleLightData.Color = LightData.Color;
SimpleLightData.FalloffExponent = LightData.FalloffExponent;
SimpleLightData.bInverseSquared = LightData.bInverseSquared;
DirectLighting += GetSimpleDynamicLighting(TranslatedWorldPosition, CameraVector, ScreenSpaceData.GBuffer.WorldNormal, ScreenSpaceData.AmbientOcclusion, ScreenSpaceData.GBuffer.DiffuseColor, ScreenSpaceData.GBuffer.SpecularColor, ScreenSpaceData.GBuffer.Roughness, SimpleLightData);
#endif // SUBSTRATE_ENABLED
}
// For debugging
//DirectLighting = InLightGridData.NumLights / (float)ForwardLightStruct.MaxCulledLightsPerCell;
return DirectLighting;
}
#if USE_PASS_PER_SHADING_MODEL
// NOTE: this is a macro since we want to be sure the 'ScreenSpaceData.GBuffer.ShadingModelID = ShadingModelId' is compile time constant
// The rest sort of follows.
// NOTE 2: The screen-space data is loaded inside the if to try to minimize register pressure, but not clear if it is respected.
#define GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(ShadingModelId, PixelShadingModelId, /*inout float4*/ CompositedLighting, ScreenUV, LightGridHeader, Dither) \
{ \
BRANCH \
if (PixelShadingModelId == ShadingModelId) \
{ \
float2 ScreenPosition = UVAndScreenPos.zw; \
FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(ScreenUV); \
float SceneDepth = CalcSceneDepth(ScreenUV); \
float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, SceneDepth), SceneDepth, 1), PrimaryView.ScreenToTranslatedWorld).xyz; \
float3 CameraVector = normalize(TranslatedWorldPosition - PrimaryView.TranslatedWorldCameraOrigin); \
ScreenSpaceData.GBuffer.ShadingModelID = ShadingModelId; \
CompositedLighting += GetLightGridLocalLighting(ScreenSpaceData, LightGridHeader, TranslatedWorldPosition, CameraVector, ScreenUV, SceneDepth, 0, Dither); \
} \
}
#endif // USE_PASS_PER_SHADING_MODEL
#if SUBTRATE_GBUFFER_FORMAT==1 && USE_HAIR_LIGHTING == 0
void ClusteredShadingPixelShader(
float2 ScreenUV : TEXCOORD0,
float3 ScreenVector : TEXCOORD1,
in float4 SvPosition : SV_Position,
out float4 OutColor : SV_Target0
#if SUBSTRATE_OPAQUE_ROUGH_REFRACTION_ENABLED
, out float3 OutOpaqueRoughRefractionSceneColor : SV_Target1
, out float3 OutSubSurfaceSceneColor : SV_Target2
#endif
)
{
uint2 PixelPos = SvPosition.xy;
const float Dither = InterleavedGradientNoise(SvPosition.xy, View.StateFrameIndexMod8);
const uint EyeIndex = 0;
const float SceneDepth = CalcSceneDepth(ScreenUV);
const float2 LocalPosition = SvPosition.xy - View.ViewRectMin.xy;
const uint GridIndex = ComputeLightGridCellIndex(uint2(LocalPosition.x, LocalPosition.y), SceneDepth, EyeIndex);
const FCulledLightsGridHeader CulledLightGridHeader = GetCulledLightsGridHeader(GridIndex);
// Debug output
#if GRID_DEBUG_ENABLE
if (GetClusterDebugColor(LocalPosition, SceneDepth, CulledLightGridHeader, OutColor)) return;
#endif
FSubstrateAddressing SubstrateAddressing = GetSubstratePixelDataByteOffset(PixelPos, uint2(View.BufferSizeAndInvSize.xy), Substrate.MaxBytesPerPixel);
FSubstratePixelHeader SubstratePixelHeader = UnpackSubstrateHeaderIn(Substrate.MaterialTextureArray, SubstrateAddressing, Substrate.TopLayerTexture);
// NOTE: this early out helps with the case where there are no lights in the grid cell.
if (CulledLightGridHeader.NumLights == 0 || !SubstratePixelHeader.IsSubstrateMaterial())
{
OutColor = 0;
return;
}
float4 CompositedLighting = 0;
float3 OpaqueRoughRefractionSceneColor = 0;
float3 SubSurfaceSceneColor = 0;
BRANCH
if (SubstratePixelHeader.ClosureCount > 0 || CulledLightGridHeader.NumLights == 0)
{
// Subtrate tile are expressed as buffer dimension rather than view dimension
const float2 ViewportScreenUV = ScreenUV * View.BufferSizeAndInvSize.xy * View.ViewSizeAndInvSize.zw;
const float2 ScreenPosition = ViewportUVToScreenPos(ViewportScreenUV);
const float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, SceneDepth), SceneDepth, 1), PrimaryView.ScreenToTranslatedWorld).xyz;
const float3 CameraVector = normalize(TranslatedWorldPosition - PrimaryView.TranslatedWorldCameraOrigin);
// Regular lights
CompositedLighting += GetLightGridLocalLighting(SubstrateAddressing, SubstratePixelHeader, OpaqueRoughRefractionSceneColor, SubSurfaceSceneColor,
CulledLightGridHeader, TranslatedWorldPosition, CameraVector, ScreenUV, SceneDepth, EyeIndex, Dither);
}
#if !VISUALIZE_LIGHT_CULLING
CompositedLighting *= View.PreExposure;
#endif
#if SUBSTRATE_OPAQUE_ROUGH_REFRACTION_ENABLED
OutOpaqueRoughRefractionSceneColor = OpaqueRoughRefractionSceneColor * View.PreExposure;
OutSubSurfaceSceneColor = SubSurfaceSceneColor * View.PreExposure;
#endif
OutColor = CompositedLighting;
}
#elif !USE_HAIR_LIGHTING
void ClusteredShadingPixelShader(
in noperspective float4 UVAndScreenPos : TEXCOORD0,
in float4 SvPosition : SV_Position,
out float4 OutColor : SV_Target0)
{
float2 ScreenUV = UVAndScreenPos.xy;
float2 ScreenPosition = UVAndScreenPos.zw;
uint PixelShadingModelID = GetScreenSpaceData(ScreenUV).GBuffer.ShadingModelID;
const float Dither = InterleavedGradientNoise(SvPosition.xy, View.StateFrameIndexMod8);
// ?
const uint EyeIndex = 0;
float SceneDepth = CalcSceneDepth(ScreenUV);
float2 LocalPosition = SvPosition.xy - View.ViewRectMin.xy;
uint GridIndex = ComputeLightGridCellIndex(uint2(LocalPosition.x, LocalPosition.y), SceneDepth, EyeIndex);
const FCulledLightsGridHeader CulledLightGridHeader = GetCulledLightsGridHeader(GridIndex);
// Debug output
#if GRID_DEBUG_ENABLE
if (GetClusterDebugColor(LocalPosition, SceneDepth, CulledLightGridHeader, OutColor)) return;
#endif
// NOTE: this early out helps with the case where there are no lights in the grid cell.
if (CulledLightGridHeader.NumLights == 0 || PixelShadingModelID == SHADINGMODELID_UNLIT)
{
OutColor = 0;
return;
}
float4 CompositedLighting = 0;
float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, SceneDepth), SceneDepth, 1), PrimaryView.ScreenToTranslatedWorld).xyz;
float3 CameraVector = normalize(TranslatedWorldPosition - PrimaryView.TranslatedWorldCameraOrigin);
uint FirstNonSimpleLightIndex = 0;
// Regular lights
#if USE_PASS_PER_SHADING_MODEL
GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_DEFAULT_LIT, PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridHeader, Dither);
GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_SUBSURFACE, PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridHeader, Dither);
GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_PREINTEGRATED_SKIN, PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridHeader, Dither);
GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_CLEAR_COAT, PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridHeader, Dither);
GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_SUBSURFACE_PROFILE, PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridHeader, Dither);
GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_TWOSIDED_FOLIAGE, PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridHeader, Dither);
GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_HAIR, PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridHeader, Dither);
GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_CLOTH, PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridHeader, Dither);
GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_EYE, PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridHeader, Dither);
GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_SINGLELAYERWATER, PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridHeader, Dither);
// SHADINGMODELID_THIN_TRANSLUCENT - skipping because it can not be opaque
#else // !USE_PASS_PER_SHADING_MODEL
CompositedLighting += GetLightGridLocalLighting(GetScreenSpaceData(ScreenUV), CulledLightGridHeader, TranslatedWorldPosition, CameraVector, ScreenUV, SceneDepth, 0, Dither);
#endif // USE_PASS_PER_SHADING_MODEL
#if !VISUALIZE_LIGHT_CULLING
CompositedLighting *= View.PreExposure;
#endif
OutColor = CompositedLighting;
}
#else // USE_HAIR_LIGHTING
void ClusteredShadingPixelShader(
in float4 SvPosition: SV_Position,
nointerpolation in uint TotalSampleCount : DISPATCH_NODECOUNT,
nointerpolation in uint2 Resolution : DISPATCH_RESOLUTION,
out float4 OutColor : SV_Target0)
{
OutColor = 0;
const uint EyeIndex = 0;
const uint2 InCoord = uint2(SvPosition.xy);
const uint SampleIndex = InCoord.x + InCoord.y * Resolution.x;
if (SampleIndex >= TotalSampleCount)
{
return;
}
const uint2 PixelCoord = HairStrands.HairSampleCoords[SampleIndex];
const float2 ScreenUV = (PixelCoord + float2(0.5f, 0.5f)) / float2(View.BufferSizeAndInvSize.xy);
const float2 ScreenPosition = (ScreenUV - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
const FPackedHairSample PackedSample = HairStrands.HairSampleData[SampleIndex];
const FHairSample Sample = UnpackHairSample(PackedSample);
const float SceneDepth = ConvertFromDeviceZ(Sample.Depth);
const float2 LocalPosition = PixelCoord - View.ViewRectMin.xy;
FScreenSpaceData ScreenSpaceData = (FScreenSpaceData)0;
{
ScreenSpaceData.AmbientOcclusion = 1;
ScreenSpaceData.GBuffer = HairSampleToGBufferData(Sample, HairStrands.HairDualScatteringRoughnessOverride);
}
const float Dither = InterleavedGradientNoise(SvPosition.xy, View.StateFrameIndexMod8);
const uint GridIndex = ComputeLightGridCellIndex(uint2(LocalPosition.x, LocalPosition.y), SceneDepth, EyeIndex);
const FCulledLightsGridHeader CulledLightGridHeader = GetCulledLightsGridHeader(GridIndex);
// NOTE: this early out helps with the case where there are no lights in the grid cell.
if (CulledLightGridHeader.NumLights == 0)
{
return;
}
const float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, SceneDepth), SceneDepth, 1), PrimaryView.ScreenToTranslatedWorld).xyz;
const float3 CameraVector = normalize(TranslatedWorldPosition - PrimaryView.TranslatedWorldCameraOrigin);
float4 CompositedLighting = 0;
CompositedLighting += GetLightGridLocalLighting(ScreenSpaceData, CulledLightGridHeader, TranslatedWorldPosition, CameraVector, ScreenUV, SceneDepth, 0, Dither, SampleIndex, TotalSampleCount, PixelCoord, Sample.LightChannelMask);
#if !VISUALIZE_LIGHT_CULLING
CompositedLighting *= View.PreExposure;
#endif
// Weight hair sample with its coverage
const float LocalCoverage = From8bitCoverage(Sample.Coverage8bit);
OutColor.rgb = CompositedLighting.xyz * LocalCoverage;
OutColor.a = LocalCoverage;
}
#endif // USE_HAIR_LIGHTING