252 lines
7.9 KiB
HLSL
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;
|
|
}
|