// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= LightGridCommon.ush =============================================================================*/ // TODO: This is clearly not the right place for these defines! Ideally ought to be exposed from engine somehow... #define LIGHT_TYPE_DIRECTIONAL 0 #define LIGHT_TYPE_POINT 1 #define LIGHT_TYPE_SPOT 2 #define LIGHT_TYPE_RECT 3 #define LIGHT_TYPE_MAX 4 #if INSTANCED_STEREO #if MATERIALBLENDING_ANY_TRANSLUCENT #define ForwardLightDataISR TranslucentBasePass.Shared.ForwardISR #else #define ForwardLightDataISR OpaqueBasePass.Shared.ForwardISR #endif #endif struct FLightGridData { uint LightGridPixelSizeShift; float3 LightGridZParams; int3 CulledGridSize; }; FLightGridData GetLightGridData(uint EyeIndex) { FLightGridData Result; #if INSTANCED_STEREO BRANCH if (EyeIndex == 0) { #endif Result.LightGridPixelSizeShift = ForwardLightData.LightGridPixelSizeShift; Result.LightGridZParams = ForwardLightData.LightGridZParams; Result.CulledGridSize = ForwardLightData.CulledGridSize; #if INSTANCED_STEREO } else { Result.LightGridPixelSizeShift = ForwardLightDataISR.LightGridPixelSizeShift; Result.LightGridZParams = ForwardLightDataISR.LightGridZParams; Result.CulledGridSize = ForwardLightDataISR.CulledGridSize; } #endif return Result; } uint3 ComputeLightGridCellCoordinate(uint2 PixelPos, float SceneDepth, uint EyeIndex) { const FLightGridData GridData = GetLightGridData(EyeIndex); uint ZSlice = (uint)(max(0, log2(SceneDepth * GridData.LightGridZParams.x + GridData.LightGridZParams.y) * GridData.LightGridZParams.z)); ZSlice = min(ZSlice, (uint)(GridData.CulledGridSize.z - 1)); return uint3(PixelPos >> GridData.LightGridPixelSizeShift, ZSlice); } uint ComputeLightGridCellIndex(uint3 GridCoordinate, uint EyeIndex) { const FLightGridData GridData = GetLightGridData(EyeIndex); return (GridCoordinate.z * GridData.CulledGridSize.y + GridCoordinate.y) * GridData.CulledGridSize.x + GridCoordinate.x; } uint ComputeLightGridCellIndex(uint2 PixelPos, float SceneDepth, uint EyeIndex) { return ComputeLightGridCellIndex(ComputeLightGridCellCoordinate(PixelPos, SceneDepth, EyeIndex), EyeIndex); } uint ComputeLightGridCellIndex(uint2 PixelPos, float SceneDepth) { return ComputeLightGridCellIndex(PixelPos, SceneDepth, 0); } #ifndef NUM_CULLED_LIGHTS_GRID_STRIDE #define NUM_CULLED_LIGHTS_GRID_STRIDE 0 #endif #ifndef LOCAL_LIGHT_DATA_STRIDE #define LOCAL_LIGHT_DATA_STRIDE 0 #endif uint GetNumLocalLights(uint EyeIndex) { #if INSTANCED_STEREO return (EyeIndex == 0) ? ForwardLightData.NumLocalLights : ForwardLightDataISR.NumLocalLights; #else return ForwardLightData.NumLocalLights; #endif } struct FCulledLightsGridData { uint NumLocalLights; uint DataStartIndex; }; FCulledLightsGridData GetCulledLightsGrid(uint GridIndex, uint EyeIndex) { FCulledLightsGridData Result; #if INSTANCED_STEREO BRANCH if (EyeIndex == 0) { #endif Result.NumLocalLights = min(ForwardLightData.NumCulledLightsGrid[GridIndex * NUM_CULLED_LIGHTS_GRID_STRIDE + 0], ForwardLightData.NumLocalLights); Result.DataStartIndex = ForwardLightData.NumCulledLightsGrid[GridIndex * NUM_CULLED_LIGHTS_GRID_STRIDE + 1]; #if INSTANCED_STEREO } else { Result.NumLocalLights = min(ForwardLightDataISR.NumCulledLightsGrid[GridIndex * NUM_CULLED_LIGHTS_GRID_STRIDE + 0], ForwardLightDataISR.NumLocalLights); Result.DataStartIndex = ForwardLightDataISR.NumCulledLightsGrid[GridIndex * NUM_CULLED_LIGHTS_GRID_STRIDE + 1]; } #endif return Result; } struct FDirectionalLightData { uint HasDirectionalLight; uint DirectionalLightShadowMapChannelMask; float2 DirectionalLightDistanceFadeMAD; float3 DirectionalLightColor; float3 DirectionalLightDirection; }; FDirectionalLightData GetDirectionalLightData(uint EyeIndex) { FDirectionalLightData Result; #if INSTANCED_STEREO BRANCH if (EyeIndex == 0) { #endif Result.HasDirectionalLight = ForwardLightData.HasDirectionalLight; Result.DirectionalLightShadowMapChannelMask = ForwardLightData.DirectionalLightShadowMapChannelMask; Result.DirectionalLightDistanceFadeMAD = ForwardLightData.DirectionalLightDistanceFadeMAD; Result.DirectionalLightColor = ForwardLightData.DirectionalLightColor; Result.DirectionalLightDirection = ForwardLightData.DirectionalLightDirection; #if INSTANCED_STEREO } else { Result.HasDirectionalLight = ForwardLightDataISR.HasDirectionalLight; Result.DirectionalLightShadowMapChannelMask = ForwardLightDataISR.DirectionalLightShadowMapChannelMask; Result.DirectionalLightDistanceFadeMAD = ForwardLightDataISR.DirectionalLightDistanceFadeMAD; Result.DirectionalLightColor = ForwardLightDataISR.DirectionalLightColor; Result.DirectionalLightDirection = ForwardLightDataISR.DirectionalLightDirection; } #endif return Result; } struct FLocalLightData { float4 LightPositionAndInvRadius; float4 LightColorAndFalloffExponent; float4 SpotAnglesAndSourceRadiusPacked; float4 LightDirectionAndShadowMask; float4 LightTangentAndSoftSourceRadius; float4 RectBarnDoorAndVirtualShadowMapId; /** Flag is true if clustered deferred is supported for this light. They are always first / together.*/ bool bClusteredDeferredSupported; /** Flag is true if it is a simple light. They are always first / together.*/ bool bIsSimpleLight; /** Virtual shadow map ID or INDEX_NONE if not present. */ int VirtualShadowMapId; }; FLocalLightData GetLocalLightData(uint GridIndex, uint EyeIndex) { FLocalLightData Result; #if INSTANCED_STEREO BRANCH if (EyeIndex == 0) { #endif uint LocalLightIndex = ForwardLightData.CulledLightDataGrid[GridIndex]; uint LocalLightBaseIndex = LocalLightIndex * LOCAL_LIGHT_DATA_STRIDE; Result.bClusteredDeferredSupported = LocalLightIndex < ForwardLightData.ClusteredDeferredSupportedEndIndex; Result.bIsSimpleLight = LocalLightIndex < ForwardLightData.SimpleLightsEndIndex; Result.LightPositionAndInvRadius = ForwardLightData.ForwardLocalLightBuffer[LocalLightBaseIndex + 0]; Result.LightColorAndFalloffExponent = ForwardLightData.ForwardLocalLightBuffer[LocalLightBaseIndex + 1]; Result.LightDirectionAndShadowMask = ForwardLightData.ForwardLocalLightBuffer[LocalLightBaseIndex + 2]; Result.SpotAnglesAndSourceRadiusPacked = ForwardLightData.ForwardLocalLightBuffer[LocalLightBaseIndex + 3]; Result.LightTangentAndSoftSourceRadius = ForwardLightData.ForwardLocalLightBuffer[LocalLightBaseIndex + 4]; Result.RectBarnDoorAndVirtualShadowMapId = ForwardLightData.ForwardLocalLightBuffer[LocalLightBaseIndex + 5]; Result.VirtualShadowMapId = int(Result.RectBarnDoorAndVirtualShadowMapId.z); #if INSTANCED_STEREO } else { uint LocalLightIndex = ForwardLightDataISR.CulledLightDataGrid[GridIndex]; uint LocalLightBaseIndex = LocalLightIndex * LOCAL_LIGHT_DATA_STRIDE; Result.bClusteredDeferredSupported = LocalLightIndex < ForwardLightDataISR.ClusteredDeferredSupportedEndIndex; Result.bIsSimpleLight = LocalLightIndex < ForwardLightDataISR.SimpleLightsEndIndex; Result.LightPositionAndInvRadius = ForwardLightDataISR.ForwardLocalLightBuffer[LocalLightBaseIndex + 0]; Result.LightColorAndFalloffExponent = ForwardLightDataISR.ForwardLocalLightBuffer[LocalLightBaseIndex + 1]; Result.LightDirectionAndShadowMask = ForwardLightDataISR.ForwardLocalLightBuffer[LocalLightBaseIndex + 2]; Result.SpotAnglesAndSourceRadiusPacked = ForwardLightDataISR.ForwardLocalLightBuffer[LocalLightBaseIndex + 3]; Result.LightTangentAndSoftSourceRadius = ForwardLightDataISR.ForwardLocalLightBuffer[LocalLightBaseIndex + 4]; Result.RectBarnDoorAndVirtualShadowMapId = ForwardLightDataISR.ForwardLocalLightBuffer[LocalLightBaseIndex + 5]; Result.VirtualShadowMapId = int(Result.RectBarnDoorAndVirtualShadowMapId.z); } #endif return Result; } /** * Helper to unpack the shadow mask part of the FLocalLightData::LightDirectionAndShadowMask.w */ float4 UnpackShadowMapChannelMask(uint ShadowMapChannelMaskPacked) { return float4((ShadowMapChannelMaskPacked & 1) ? 1.0f : 0.0f, (ShadowMapChannelMaskPacked & 2) ? 1.0f : 0.0f, (ShadowMapChannelMaskPacked & 4) ? 1.0f : 0.0f, (ShadowMapChannelMaskPacked & 8) ? 1.0f : 0.0f); } /** * Helpers to pack/unpack the shadow mask for cluster shading and virtual shadow map * Currently hard-coded for 4 bits per light, up to 32 lights per pixel in a uint4 */ uint4 InitializePackedShadowMask() { return uint(0xffffffff).xxxx; } uint GetPackedShadowMaskMaxLightCount() { return VirtualShadowMap.PackedShadowMaskMaxLightCount; } void PackShadowMask(inout uint4 InOutShadowMask, float InShadowFactor, uint InLightIndex) { uint Value = ~uint(round(InShadowFactor * 15.0f)) & 15U; uint Dword = InLightIndex / 8; InOutShadowMask.x ^= (Dword == 0U) ? (Value << (InLightIndex ) * 4U) : 0U; InOutShadowMask.y ^= (Dword == 1U) ? (Value << (InLightIndex - 8U) * 4U) : 0U; InOutShadowMask.z ^= (Dword == 2U) ? (Value << (InLightIndex - 16U) * 4U) : 0U; InOutShadowMask.w ^= (Dword == 3U) ? (Value << (InLightIndex - 24U) * 4U) : 0U; //OutShadowMask ^= (~uint(round(InShadowFactor * 7.0)) & 7u) << (InLightIndex * 3u); } float UnpackShadowMask(uint4 InShadowMask, uint InLightIndex) { uint Dword = InLightIndex / 8; return ((InShadowMask[Dword] >> (InLightIndex - Dword*8U) * 4U) & 15U) / 15.0f; //return ((InShadowMask >> (InLightIndex * 3u)) & 7u) / 7.0; } // Unpack with dither to hide some of the quantization float UnpackShadowMask(uint4 InShadowMask, uint InLightIndex, float Dither) { float ShadowFactor = UnpackShadowMask(InShadowMask, InLightIndex); if (ShadowFactor > 0.0f && ShadowFactor < 1.0f) { ShadowFactor = saturate(ShadowFactor + (Dither - 0.5f) * (1.0f / 16.0f)); } return ShadowFactor; }