355 lines
15 KiB
HLSL
355 lines
15 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;
|
|
uint ShadingPassIndex;
|
|
|
|
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 !TRANSLUCENCY_LIGHTING_VOLUME
|
|
// Temporarily reuse bGuidedAsVisible as bCastVolumetricShadow since it's not currently used for volumes
|
|
if (!LightSample.bGuidedAsVisible)
|
|
{
|
|
LightSample.bVisible = true;
|
|
}
|
|
#endif
|
|
|
|
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 !TRANSLUCENCY_LIGHTING_VOLUME
|
|
// Temporarily reuse bGuidedAsVisible as bCastVolumetricShadow since it's not currently used for volumes
|
|
if (!LightSample.bGuidedAsVisible)
|
|
{
|
|
LightSample.bVisible = true;
|
|
}
|
|
#endif
|
|
|
|
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));
|
|
|
|
const uint3 OutputVolumeCoord = VolumeCoord;
|
|
|
|
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));
|
|
}
|
|
|
|
#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 RESAMPLE_VOLUME
|
|
{
|
|
float4 NDCPosition = mul(float4(TranslatedWorldPosition, 1), UnjitteredTranslatedWorldToClip);
|
|
NDCPosition.xy /= NDCPosition.w;
|
|
|
|
float3 ResampleUV = float3(NDCPosition.xy * float2(.5f, -.5f) + .5f, ComputeNormalizedZSliceFromDepth(ResampleVolumeZParams, ResampleVolumeInvBufferSize.z, NDCPosition.w));
|
|
VolumeCoord = floor(saturate(ResampleUV) * ResampleVolumeViewSize);
|
|
}
|
|
#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(OutputVolumeCoord, 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, TEXT("StOffset0: "));
|
|
Print(DebugContext, StochasticTrilinearOffset0, FontValue, 4, 1);
|
|
Newline(DebugContext);
|
|
Print(DebugContext, TEXT("StOffset1: "));
|
|
Print(DebugContext, StochasticTrilinearOffset1, FontValue, 4, 1);
|
|
}
|
|
#endif
|
|
|
|
if (DebugContext.bIsActive)
|
|
{
|
|
Newline(DebugContext);
|
|
Print(DebugContext, TEXT("LightId | Weight | Lighting"), FontSilver);
|
|
}
|
|
|
|
#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 TRANSLUCENCY_LIGHTING_VOLUME
|
|
if (UnpackAffectsTranslucentLighting(ForwardLightData) == 0)
|
|
{
|
|
SampleWeight = 0.0f;
|
|
}
|
|
#endif // TRANSLUCENCY_LIGHTING_VOLUME
|
|
|
|
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);
|
|
const bool bUnifiedVolumeSampling = false;
|
|
float3 Lighting = GetMegaLightsVolumeLighting(TranslatedWorldPosition, CameraVector, DistanceBiasSqr, LightVolumetricSoftFadeDistance, LightData, VolumetricScatteringIntensity, bUnifiedVolumeSampling, L) * SampleWeight;
|
|
LightScattering += Lighting;
|
|
|
|
const float DirectionalLightContribution = 0.0f;
|
|
AccumulateSHLighting(L, Lighting, DirectionalLightContribution, LightAmbient, LightDirectional);
|
|
|
|
if (DebugContext.bIsActive)
|
|
{
|
|
Print(DebugContext, Lighting, FontValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DebugContext.bIsActive)
|
|
{
|
|
Newline(DebugContext);
|
|
Print(DebugContext, TEXT("Lighting: "));
|
|
Print(DebugContext, LightScattering, FontValue);
|
|
AddTextBackground(DebugContext, FontBackground);
|
|
}
|
|
|
|
#if REFERENCE_MODE
|
|
//This is not the first shading pass, hence we try to accumulate from previous one
|
|
if (ShadingPassIndex > 0)
|
|
{
|
|
float BlendFactor = 1.0f / float(ShadingPassIndex + 1.0f); //True as all pass have the same number of samples
|
|
|
|
#if TRANSLUCENCY_LIGHTING_VOLUME
|
|
const float4 PrevLightingAmbient = RWTranslucencyVolumeResolvedLightingAmbient[OutputVolumeCoord];
|
|
const float4 PrevLightingDirectional = RWTranslucencyVolumeResolvedLightingDirectional[OutputVolumeCoord];
|
|
LightAmbient = PrevLightingAmbient * (1.0f - BlendFactor) + LightAmbient * BlendFactor;
|
|
LightDirectional = PrevLightingDirectional * (1.0f - BlendFactor) + LightDirectional * BlendFactor;
|
|
#else
|
|
const float3 PrevLightScattering = RWVolumeResolvedLighting[OutputVolumeCoord] / View.PreExposure;
|
|
LightScattering = PrevLightScattering * (1.0f - BlendFactor) + LightScattering * BlendFactor;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if TRANSLUCENCY_LIGHTING_VOLUME
|
|
if (TranslucencyVolumeCascadeIndex == 1)
|
|
{
|
|
float FinalLerpFactor = GetBorderLerpFactor(OutputVolumeCoord, TranslucencyVolumeCascadeIndex);
|
|
|
|
if (FinalLerpFactor < 1)
|
|
{
|
|
float4 FallbackAmbient = 0;
|
|
float4 FallbackDirectional = 0;
|
|
|
|
for (uint DirectionalLightIndex = ForwardLightStruct.DirectionalMegaLightsSupportedStartIndex; DirectionalLightIndex < ForwardLightStruct.NumDirectionalLights; ++DirectionalLightIndex)
|
|
{
|
|
uint LightIndex = ForwardLightStruct.DirectionalLightIndices[DirectionalLightIndex];
|
|
FForwardLightData ForwardLightData = GetForwardLightData(LightIndex, 0);
|
|
checkSlow(UnpackIsHandledByMegaLights(ForwardLightData));
|
|
|
|
if (!UnpackAffectsTranslucentLighting(ForwardLightData))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FDeferredLightData LightData = ConvertToDeferredLight(ForwardLightData);
|
|
LightData.bRadialLight = false;
|
|
LightData.bInverseSquared = false;
|
|
|
|
float3 Lighting = LightData.Color / PI;
|
|
|
|
const float DirectionalLightContribution = 1.0f;
|
|
AccumulateSHLighting(LightData.Direction, Lighting, DirectionalLightContribution, FallbackAmbient, FallbackDirectional);
|
|
}
|
|
|
|
LightAmbient = lerp(FallbackAmbient, LightAmbient, FinalLerpFactor);
|
|
LightDirectional = lerp(FallbackDirectional, LightDirectional, FinalLerpFactor);
|
|
}
|
|
}
|
|
|
|
RWTranslucencyVolumeResolvedLightingAmbient[OutputVolumeCoord] = LightAmbient;
|
|
RWTranslucencyVolumeResolvedLightingDirectional[OutputVolumeCoord] = LightDirectional;
|
|
#else // !TRANSLUCENCY_LIGHTING_VOLUME
|
|
RWVolumeResolvedLighting[OutputVolumeCoord] = LightScattering * View.PreExposure;
|
|
#endif // !TRANSLUCENCY_LIGHTING_VOLUME
|
|
}
|
|
}
|