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

265 lines
12 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
// When loading SSS checkerboard pixel, do not adjust DiffuseColor/SpecularColor to preserve specular and diffuse lighting values for each pixel
#define ALLOW_SSS_MATERIAL_OVERRIDE 0
#include "../Common.ush"
#include "../SHCommon.ush"
#include "../BlueNoise.ush"
#include "MegaLights.ush"
#include "MegaLightsVolume.ush"
#include "../Lumen/LumenReflectionDenoiserCommon.ush"
#include "../StochasticLighting/StochasticLightingUpsample.ush"
float3 VolumeFrameJitterOffset;
float VolumeInverseSquaredLightDistanceBiasScale;
uint3 VolumeSampleViewSize;
uint3 DownsampledVolumeViewSize;
uint3 NumSamplesPerVoxel;
Texture3D<uint> VolumeLightSamples;
#if TRANSLUCENCY_LIGHTING_VOLUME
RWTexture3D<float4> RWTranslucencyVolumeResolvedLightingAmbient;
RWTexture3D<float4> RWTranslucencyVolumeResolvedLightingDirectional;
#else
RWTexture3D<float3> RWVolumeResolvedLighting;
#endif
float VolumeMaxShadingWeight;
uint DebugLightId;
void LoadPackedLightSamples(inout uint PackedLightSamples[NUM_SAMPLES_PER_VOXEL_1D], int3 DownsampledVolumeCoord)
{
DownsampledVolumeCoord = clamp(DownsampledVolumeCoord, int3(0, 0, 0), int3(DownsampledVolumeViewSize - 1));
for (uint SampleIndex = 0; SampleIndex < NUM_SAMPLES_PER_VOXEL_1D; ++SampleIndex)
{
uint3 LightSampleCoord = DownsampledVolumeCoord * uint3(NUM_SAMPLES_PER_VOXEL_3D_X, NUM_SAMPLES_PER_VOXEL_3D_Y, NUM_SAMPLES_PER_VOXEL_3D_Z)
+ uint3(SampleIndex % NUM_SAMPLES_PER_VOXEL_3D_X, SampleIndex / NUM_SAMPLES_PER_VOXEL_3D_X, 0);
PackedLightSamples[SampleIndex] = VolumeLightSamples[LightSampleCoord];
}
}
void AccumulateLightSample(uint PackedLightSamples[NUM_SAMPLES_PER_VOXEL_1D], inout uint NextLocalLightIndex)
{
for (uint SampleIndex = 0; SampleIndex < NUM_SAMPLES_PER_VOXEL_1D; ++SampleIndex)
{
FLightSample LightSample = UnpackLightSample(PackedLightSamples[SampleIndex]);
if (LightSample.bVisible)
{
NextLocalLightIndex = min(NextLocalLightIndex, LightSample.LocalLightIndex);
}
}
}
void AccumulateLightSample(uint PackedLightSamples[NUM_SAMPLES_PER_VOXEL_1D], uint LocalLightIndex, float UpsampleWeight, inout uint NextLocalLightIndex, inout float SampleWeightSum)
{
for (uint SampleIndex = 0; SampleIndex < NUM_SAMPLES_PER_VOXEL_1D; ++SampleIndex)
{
FLightSample LightSample = UnpackLightSample(PackedLightSamples[SampleIndex]);
if (LightSample.bVisible)
{
if (LightSample.LocalLightIndex == LocalLightIndex)
{
SampleWeightSum += LightSample.Weight * UpsampleWeight;
}
if (LightSample.LocalLightIndex > LocalLightIndex)
{
NextLocalLightIndex = min(NextLocalLightIndex, LightSample.LocalLightIndex);
}
}
}
}
/**
* Upsample light samples and apply all lights per voxel
*/
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, THREADGROUP_SIZE)]
void VolumeShadeLightSamplesCS(
uint3 GroupId : SV_GroupID,
uint3 GroupThreadId : SV_GroupThreadID,
uint3 DispatchThreadId : SV_DispatchThreadID)
{
uint3 VolumeCoord = DispatchThreadId.xyz;
if (all(VolumeCoord < VolumeViewSize))
{
FShaderPrintContext DebugContext = InitVolumeDebugContext(VolumeCoord, /*bDownsampled*/ false, float2(0.5, 0.05));
float SceneDepth = 0.0f;
const float3 TranslatedWorldPosition = ComputeCellTranslatedWorldPosition(VolumeCoord, VolumeFrameJitterOffset, SceneDepth);
const float3 CameraVector = normalize(TranslatedWorldPosition - View.TranslatedWorldCameraOrigin);
if (DebugContext.bIsActive)
{
Print(DebugContext, TEXT("VolumeShadeLightSamples"), FontTitle);
Newline(DebugContext);
Print(DebugContext, TEXT("Coord : "));
Print(DebugContext, VolumeCoord, FontValue);
Newline(DebugContext);
Print(DebugContext, TEXT("TileCoord: "));
Print(DebugContext, GroupId.xyz, FontValue);
Newline(DebugContext);
Print(DebugContext, TEXT("TWS : "));
Print(DebugContext, TranslatedWorldPosition, FontValue);
AddCrossTWS(DebugContext, TranslatedWorldPosition, 5.0f, float4(0, 0, 1, 1));
Newline(DebugContext);
Print(DebugContext, TEXT("LightId | Weight | Lighting"), FontSilver);
}
#if TRANSLUCENCY_LIGHTING_VOLUME
float DistanceBiasSqr = 0.0f;
float LightVolumetricSoftFadeDistance = 0.0f;
#else
float SceneDepth2 = 0.0f;
float SceneDepth3 = 0.0f;
float CellRadius = length(TranslatedWorldPosition - ComputeCellTranslatedWorldPosition(VolumeCoord + uint3(1, 1, 1), VolumeFrameJitterOffset, SceneDepth2));
float Cell2DRadius = length(TranslatedWorldPosition - ComputeCellTranslatedWorldPosition(VolumeCoord + uint3(1, 1, 0), VolumeFrameJitterOffset, SceneDepth3));
float LightVolumetricSoftFadeDistance = LightSoftFading * Cell2DRadius;
// Bias the inverse squared light falloff based on voxel size to prevent aliasing near the light source
float DistanceBiasSqr = max(CellRadius * VolumeInverseSquaredLightDistanceBiasScale, 1);
DistanceBiasSqr *= DistanceBiasSqr;
#endif
#if VOLUME_DOWNSAMPLE_FACTOR == 2
int3 DownsampledVolumeCoord000 = int3(VolumeCoord - 1) / VOLUME_DOWNSAMPLE_FACTOR;
int3 VolumeCoordOffset = VolumeCoord - DownsampledVolumeCoord000 * 2;
int3 SampleOffset000 = GetSampleVoxelCoordJitter(DownsampledVolumeCoord000 + uint3(0, 0, 0)) + uint3(0, 0, 0) * 2 - VolumeCoordOffset;
int3 SampleOffset100 = GetSampleVoxelCoordJitter(DownsampledVolumeCoord000 + uint3(1, 0, 0)) + uint3(1, 0, 0) * 2 - VolumeCoordOffset;
int3 SampleOffset010 = GetSampleVoxelCoordJitter(DownsampledVolumeCoord000 + uint3(0, 1, 0)) + uint3(0, 1, 0) * 2 - VolumeCoordOffset;
int3 SampleOffset110 = GetSampleVoxelCoordJitter(DownsampledVolumeCoord000 + uint3(1, 1, 0)) + uint3(1, 1, 0) * 2 - VolumeCoordOffset;
int3 SampleOffset001 = GetSampleVoxelCoordJitter(DownsampledVolumeCoord000 + uint3(0, 0, 1)) + uint3(0, 0, 1) * 2 - VolumeCoordOffset;
int3 SampleOffset101 = GetSampleVoxelCoordJitter(DownsampledVolumeCoord000 + uint3(1, 0, 1)) + uint3(1, 0, 1) * 2 - VolumeCoordOffset;
int3 SampleOffset011 = GetSampleVoxelCoordJitter(DownsampledVolumeCoord000 + uint3(0, 1, 1)) + uint3(0, 1, 1) * 2 - VolumeCoordOffset;
int3 SampleOffset111 = GetSampleVoxelCoordJitter(DownsampledVolumeCoord000 + uint3(1, 1, 1)) + uint3(1, 1, 1) * 2 - VolumeCoordOffset;
// Triangle filter weight between the shaded voxel and 8 neighbors
float4 InterpolationWeights0;
InterpolationWeights0.x = (2.0f - abs(SampleOffset000.x)) * (2.0f - abs(SampleOffset000.y)) * (2.0f - abs(SampleOffset000.z));
InterpolationWeights0.y = (2.0f - abs(SampleOffset100.x)) * (2.0f - abs(SampleOffset100.y)) * (2.0f - abs(SampleOffset100.z));
InterpolationWeights0.z = (2.0f - abs(SampleOffset010.x)) * (2.0f - abs(SampleOffset010.y)) * (2.0f - abs(SampleOffset010.z));
InterpolationWeights0.w = (2.0f - abs(SampleOffset110.x)) * (2.0f - abs(SampleOffset110.y)) * (2.0f - abs(SampleOffset110.z));
float4 InterpolationWeights1;
InterpolationWeights1.x = (2.0f - abs(SampleOffset001.x)) * (2.0f - abs(SampleOffset001.y)) * (2.0f - abs(SampleOffset001.z));
InterpolationWeights1.y = (2.0f - abs(SampleOffset101.x)) * (2.0f - abs(SampleOffset101.y)) * (2.0f - abs(SampleOffset101.z));
InterpolationWeights1.z = (2.0f - abs(SampleOffset011.x)) * (2.0f - abs(SampleOffset011.y)) * (2.0f - abs(SampleOffset011.z));
InterpolationWeights1.w = (2.0f - abs(SampleOffset111.x)) * (2.0f - abs(SampleOffset111.y)) * (2.0f - abs(SampleOffset111.z));
// Normalize weights
InterpolationWeights0 /= 8.0f;
InterpolationWeights1 /= 8.0f;
#endif
float3 LightScattering = 0.0f;
float4 LightAmbient = 0.0f;
float4 LightDirectional = 0.0f;
#if !TRANSLUCENCY_LIGHTING_VOLUME
const float FroxelFootprintMargin = 0.5f; // trilinear interpolation
if (IsFroxelVisible(VolumeCoord, FroxelFootprintMargin))
#endif
{
#if VOLUME_DOWNSAMPLE_FACTOR == 2
// Stochastic sample interpolation. Need to use at least samples 2 for good quality.
const float RandomScalar = BlueNoiseScalar(VolumeCoordToNoiseCoord(VolumeCoord), MegaLightsStateFrameIndex);
uint3 StochasticTrilinearOffset0 = GetStochasticTrilinearOffset((RandomScalar + 0.0f) / 2.0f, InterpolationWeights0, InterpolationWeights1);
uint3 StochasticTrilinearOffset1 = GetStochasticTrilinearOffset((RandomScalar + 1.0f) / 2.0f, InterpolationWeights0, InterpolationWeights1);
if (DebugContext.bIsActive)
{
Newline(DebugContext);
Print(DebugContext, StochasticTrilinearOffset0, FontValue);
Print(DebugContext, StochasticTrilinearOffset1, FontValue);
}
#endif
#if VOLUME_DOWNSAMPLE_FACTOR == 2
uint PackedLightSamples0[NUM_SAMPLES_PER_VOXEL_1D];
uint PackedLightSamples1[NUM_SAMPLES_PER_VOXEL_1D];
LoadPackedLightSamples(PackedLightSamples0, DownsampledVolumeCoord000 + StochasticTrilinearOffset0);
LoadPackedLightSamples(PackedLightSamples1, DownsampledVolumeCoord000 + StochasticTrilinearOffset1);
#else
uint PackedLightSamples0[NUM_SAMPLES_PER_VOXEL_1D];
LoadPackedLightSamples(PackedLightSamples0, VolumeCoord);
#endif
uint NextLocalLightIndex = MAX_LOCAL_LIGHT_INDEX;
AccumulateLightSample(PackedLightSamples0, NextLocalLightIndex);
#if VOLUME_DOWNSAMPLE_FACTOR == 2
AccumulateLightSample(PackedLightSamples1, NextLocalLightIndex);
#endif
while (NextLocalLightIndex < MAX_LOCAL_LIGHT_INDEX)
{
const uint LocalLightIndex = WaveActiveMin(NextLocalLightIndex);
if (LocalLightIndex == NextLocalLightIndex)
{
NextLocalLightIndex = MAX_LOCAL_LIGHT_INDEX;
float SampleWeight = 0.0f;
#if VOLUME_DOWNSAMPLE_FACTOR == 2
AccumulateLightSample(PackedLightSamples0, LocalLightIndex, 0.5f, NextLocalLightIndex, SampleWeight);
AccumulateLightSample(PackedLightSamples1, LocalLightIndex, 0.5f, NextLocalLightIndex, SampleWeight);
#else
AccumulateLightSample(PackedLightSamples0, LocalLightIndex, 1.0f, NextLocalLightIndex, SampleWeight);
#endif
SampleWeight = min(SampleWeight, VolumeMaxShadingWeight);
const FForwardLightData ForwardLightData = GetForwardLightData(LocalLightIndex, 0);
const FDeferredLightData LightData = ConvertToDeferredLight(ForwardLightData);
if (DebugContext.bIsActive)
{
Newline(DebugContext);
Print(DebugContext, ForwardLightData.LightSceneId, Select(ForwardLightData.LightSceneId == DebugLightId, FontSelected, FontValue));
Print(DebugContext, SampleWeight, FontValue);
}
if (SampleWeight > 0.01f)
{
float3 L;
const float VolumetricScatteringIntensity = UnpackVolumetricScatteringIntensity(ForwardLightData);
float3 Lighting = GetMegaLightsVolumeLighting(TranslatedWorldPosition, CameraVector, DistanceBiasSqr, LightVolumetricSoftFadeDistance, LightData, VolumetricScatteringIntensity, L) * SampleWeight;
LightScattering += Lighting;
{
FTwoBandSHVectorRGB SHLighting = MulSH(SHBasisFunction(L), Lighting);
// Directional light contribution in w
LightAmbient += float4(SHLighting.R.V.x, SHLighting.G.V.x, SHLighting.B.V.x, 0.0f);
float3 LuminanceWeights = LuminanceFactors();
float3 Coefficient0 = float3(SHLighting.R.V.y, SHLighting.G.V.y, SHLighting.B.V.y);
float3 Coefficient1 = float3(SHLighting.R.V.z, SHLighting.G.V.z, SHLighting.B.V.z);
float3 Coefficient2 = float3(SHLighting.R.V.w, SHLighting.G.V.w, SHLighting.B.V.w);
LightDirectional += float4(dot(Coefficient0, LuminanceWeights), dot(Coefficient1, LuminanceWeights), dot(Coefficient2, LuminanceWeights), 0);
}
if (DebugContext.bIsActive)
{
Print(DebugContext, Lighting, FontValue);
}
}
}
}
}
if (DebugContext.bIsActive)
{
Newline(DebugContext);
Print(DebugContext, TEXT("Lighting: "));
Print(DebugContext, LightScattering, FontValue);
}
#if TRANSLUCENCY_LIGHTING_VOLUME
RWTranslucencyVolumeResolvedLightingAmbient[VolumeCoord] = LightAmbient;
RWTranslucencyVolumeResolvedLightingDirectional[VolumeCoord] = LightDirectional;
#else
RWVolumeResolvedLighting[VolumeCoord] = LightScattering * View.PreExposure;
#endif
}
}