Files
UnrealEngine/Engine/Shaders/Private/MegaLights/MegaLightsVolume.ush
2025-05-18 13:04:45 +08:00

252 lines
7.9 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "../LightFunctionAtlas/LightFunctionAtlasCommon.usf"
#include "../Nanite/NaniteHZBCull.ush"
uint VolumeDownsampleFactorMultShift;
/**
* Returns sample jitter offset in the range [0, VOLUME_DOWNSAMPLE_FACTOR - 1]
*/
uint3 GetSampleVoxelCoordJitter(uint3 DownsampledVolumeCoord)
{
uint3 Jitter = 0;
if (VolumeDownsampleFactorMultShift > 0)
{
// uint3 CellIndex = DownsampledVolumeCoord % 2;
// uint LinearIndex = CellIndex.x + CellIndex.y * 2 + CellIndex.z * 4;
// LinearIndex = (LinearIndex + MegaLightsStateFrameIndex) % 8;
//
// // #ml_todo: investigate whether this produces a good pattern
// // 4-rooks sampling pattern
// uint3 Jitter;
// Jitter.x = LinearIndex & 0x04 ? 0 : 1;
// Jitter.y = LinearIndex & 0x02 ? 1 : 0;
// Jitter.z = LinearIndex & 0x01 ? 0 : 1;
}
return Jitter;
}
uint3 DownsampledVolumeCoordToVolumeCoord(uint3 DownsampledVolumeCoord)
{
return (DownsampledVolumeCoord << VolumeDownsampleFactorMultShift) + GetSampleVoxelCoordJitter(DownsampledVolumeCoord);
}
uint3 VolumeCoordToDownsampledVolumeCoord(uint3 VolumeCoord)
{
return VolumeCoord >> VolumeDownsampleFactorMultShift;
}
uint2 VolumeCoordToNoiseCoord(uint3 VolumeCoord)
{
// #ml_todo: hard coded for BlueNoise 128x128, but is used for other things too
// https://github.com/electronicarts/fastnoise/blob/main/FastNoiseDesign.md
return VolumeCoord.xy + R2Sequence(VolumeCoord.z) * 128;
}
uint3 VolumeViewSize;
float LightSoftFading;
#if TRANSLUCENCY_LIGHTING_VOLUME
uint TranslucencyVolumeCascadeIndex;
float TranslucencyVolumeInvResolution;
float3 ComputeCellTranslatedWorldPosition(uint3 GridCoordinate, float3 CellOffset, out float SceneDepth)
{
SceneDepth = 0.0f;
return View.TranslucencyLightingVolumeMin[TranslucencyVolumeCascadeIndex].xyz + (GridCoordinate + 0.5f) * View.TranslucencyLightingVolumeInvSize[TranslucencyVolumeCascadeIndex].w;
}
#else // !TRANSLUCENCY_LIGHTING_VOLUME
float4x4 UnjitteredClipToTranslatedWorld;
float4x4 UnjitteredPrevTranslatedWorldToClip;
float3 MegaLightsVolumeZParams;
uint MegaLightsVolumePixelSize;
float ComputeDepthFromZSlice(float ZSlice)
{
return ComputeDepthFromZSlice(MegaLightsVolumeZParams, ZSlice);
}
float ComputeZSliceFromDepth(float SliceDepth)
{
return ComputeZSliceFromDepth(MegaLightsVolumeZParams, SliceDepth);
}
float3 ComputeCellTranslatedWorldPosition(uint3 GridCoordinate, float3 CellOffset, out float SceneDepth)
{
float2 VolumeUV = (GridCoordinate.xy + CellOffset.xy) / float2(VolumeViewSize.xy);
float2 VolumeNDC = (VolumeUV * 2 - 1) * float2(1, -1);
SceneDepth = ComputeDepthFromZSlice(max(GridCoordinate.z + CellOffset.z, 0));
float TileDeviceZ = ConvertToDeviceZ(SceneDepth);
float4 CenterPosition = mul(float4(VolumeNDC, TileDeviceZ, 1), UnjitteredClipToTranslatedWorld);
return CenterPosition.xyz / CenterPosition.w;
}
#endif // !TRANSLUCENCY_LIGHTING_VOLUME
float VolumePhaseG;
float3 GetMegaLightsVolumeLighting(
float3 TranslatedWorldPosition,
float3 CameraVector,
float DistanceBiasSqr,
float LightVolumetricSoftFadeDistance,
FDeferredLightData LightData,
float VolumetricScatteringIntensity,
out float3 OutL)
{
float3 Lighting = 0.0f;
OutL = 0.0f;
#if !TRANSLUCENCY_LIGHTING_VOLUME
if (VolumetricScatteringIntensity > 0.0f)
#endif
{
const bool bSoftFadeEnabled = LightSoftFading > 0;
float3 L = LightData.Direction;
float3 ToLight = L;
float LightMask = 1.0f;
if (LightData.bRadialLight)
{
LightMask = GetLocalLightAttenuation(TranslatedWorldPosition, LightData, ToLight, L);
}
OutL = L;
float Attenuation;
if (LightData.bRectLight)
{
FRect Rect = GetRect(ToLight, LightData);
float SoftFade = 1.0f;
#if USE_LIGHT_SOFT_FADING
if (bSoftFadeEnabled)
{
SoftFade *= GetRectLightVolumetricSoftFading(LightData, Rect, LightVolumetricSoftFadeDistance, ToLight);
}
#endif
Attenuation = SoftFade * IntegrateLight(Rect);
}
else
{
FCapsuleLight Capsule = GetCapsule(ToLight, LightData);
Capsule.DistBiasSqr = DistanceBiasSqr;
float SoftFade = 1.0f;
#if USE_LIGHT_SOFT_FADING
if (LightData.bSpotLight && bSoftFadeEnabled)
{
SoftFade *= GetSpotLightVolumetricSoftFading(LightData, LightVolumetricSoftFadeDistance, ToLight);
}
#endif
Attenuation = SoftFade * IntegrateLight(Capsule, LightData.bInverseSquared);
}
FLightFunctionColor LightFunctionColor = 1.0f;
#if USE_LIGHT_FUNCTION_ATLAS
LightFunctionColor = GetLocalLightFunctionCommon(TranslatedWorldPosition, LightData.LightFunctionAtlasLightIndex);
#endif
float CombinedAttenuation = Attenuation * LightMask;
#if TRANSLUCENCY_LIGHTING_VOLUME
Lighting += LightData.Color * LightFunctionColor * CombinedAttenuation / PI;
#else
Lighting += LightData.Color * LightFunctionColor * (HenyeyGreensteinPhase(VolumePhaseG, dot(L, -CameraVector)) * CombinedAttenuation * VolumetricScatteringIntensity);
#endif
}
return Lighting;
}
uint UseHZBOcclusionTest;
#if !TRANSLUCENCY_LIGHTING_VOLUME
FScreenRect ComputeFroxelCullRect(uint3 GridCoordinate, float MinTileZ, float MaxTileZ, float FootprintMargin)
{
// Compute extent of tiles in clip-space. Note that the last tile may extend a bit outside of view if view size is not evenly divisible tile size.
const float2 InvCulledGridSizeF = MegaLightsVolumePixelSize * View.ViewSizeAndInvSize.zw;
const float2 TileSize = float2(2.0f, -2.0f) * InvCulledGridSizeF.xy;
const float2 UnitPlaneMin = float2(-1.0f, 1.0f);
float2 UnitPlaneTileMin = (GridCoordinate.xy - FootprintMargin) * TileSize + UnitPlaneMin;
float2 UnitPlaneTileMax = (GridCoordinate.xy + 1 + FootprintMargin) * TileSize + UnitPlaneMin;
float MinTileDeviceZ = ConvertToDeviceZ(MinTileZ);
float MaxTileDeviceZ = ConvertToDeviceZ(MaxTileZ);
float3 CullRectMin;
CullRectMin.x = min(UnitPlaneTileMin.x, UnitPlaneTileMax.x);
CullRectMin.y = min(UnitPlaneTileMin.y, UnitPlaneTileMax.y);
CullRectMin.z = min(MinTileDeviceZ, MaxTileDeviceZ);
float3 CullRectMax;
CullRectMax.x = max(UnitPlaneTileMin.x, UnitPlaneTileMax.x);
CullRectMax.y = max(UnitPlaneTileMin.y, UnitPlaneTileMax.y);
CullRectMax.z = max(MinTileDeviceZ, MaxTileDeviceZ);
return GetScreenRect(int4(0, 0, HZBViewSize), CullRectMin, CullRectMax, 4);
}
bool IsFroxelVisible(uint3 GridCoordinate, float FootprintMargin)
{
if (UseHZBOcclusionTest != 0)
{
float MinTileZ = ComputeDepthFromZSlice(max(0, GridCoordinate.z + 0 - FootprintMargin));
float MaxTileZ = ComputeDepthFromZSlice(min(VolumeViewSize.z, GridCoordinate.z + 1 + FootprintMargin));
FScreenRect Rect = ComputeFroxelCullRect(GridCoordinate, MinTileZ, MaxTileZ, FootprintMargin);
return IsVisibleHZB(Rect, true /*bSample4x4*/);
}
return true;
}
#endif
int VolumeDebugSliceIndex;
FShaderPrintContext InitVolumeDebugContext(uint3 VolumeCoord, bool bDownsampled, float2 StartPos)
{
FShaderPrintContext DebugContext;
#if DEBUG_MODE
#if TRANSLUCENCY_LIGHTING_VOLUME
const uint2 DebugScreenCoord = GetDebugScreenCoord();
const float2 DebugScreenUV = (DebugScreenCoord + 0.5f) * View.BufferSizeAndInvSize.zw;
const float SceneDepth = CalcSceneDepth(DebugScreenUV);
float3 TranslatedWorldPosition = GetTranslatedWorldPositionFromScreenUV(DebugScreenUV, SceneDepth);
int3 DebugVoxelCoord = (TranslatedWorldPosition - View.TranslucencyLightingVolumeMin[TranslucencyVolumeCascadeIndex].xyz) / View.TranslucencyLightingVolumeInvSize[TranslucencyVolumeCascadeIndex].w;
DebugVoxelCoord = DebugVoxelCoord >> (bDownsampled ? VolumeDownsampleFactorMultShift : 0);
DebugVoxelCoord.z += 1;
if (TranslucencyVolumeCascadeIndex != 0)
{
DebugVoxelCoord = -1;
}
#else // !TRANSLUCENCY_LIGHTING_VOLUME
int3 DebugVoxelCoord = int3(GetDebugScreenCoord() / MegaLightsVolumePixelSize, VolumeDebugSliceIndex);
if (bDownsampled)
{
DebugVoxelCoord = DebugVoxelCoord >> VolumeDownsampleFactorMultShift;
}
#endif // !TRANSLUCENCY_LIGHTING_VOLUME
DebugContext = InitShaderPrintContext(all(VolumeCoord == DebugVoxelCoord), StartPos);
#else
DebugContext = InitShaderPrintContext(false, StartPos);
#endif
return DebugContext;
}