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

593 lines
18 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#define SUPPORT_CONTACT_SHADOWS 0
#define DYNAMICALLY_SHADOWED 1
#define TREAT_MAXDEPTH_UNSHADOWED 1
#define SHADOW_QUALITY 2
#define NO_TRANSLUCENCY_AVAILABLE
#include "/Engine/Generated/Material.ush"
#include "HeterogeneousVolumesStochasticFiltering.ush"
#include "HeterogeneousVolumesLiveShadingUtils.ush"
#include "HeterogeneousVolumesTracingUtils.ush"
#include "HeterogeneousVolumesAdaptiveVolumetricShadowMapUtils.ush"
#include "../BlueNoise.ush"
#ifndef THREADGROUP_SIZE_1D
#define THREADGROUP_SIZE_1D 1
#endif // THREADGROUP_SIZE_1D
#ifndef THREADGROUP_SIZE_2D
#define THREADGROUP_SIZE_2D 1
#endif // THREADGROUP_SIZE_2D
#ifndef THREADGROUP_SIZE_3D
#define THREADGROUP_SIZE_3D 1
#endif // THREADGROUP_SIZE_3D
// Object data
float4x4 LocalToWorld;
float4x4 WorldToLocal;
float3 LocalBoundsOrigin;
float3 LocalBoundsExtent;
int PrimitiveId;
// Volume data
int3 VoxelResolution;
// Ray data
float MaxTraceDistance;
float MaxShadowTraceDistance;
float StepSize;
float StepFactor;
float ShadowStepSize;
float ShadowStepFactor;
int MaxStepCount;
int bJitter;
int StochasticFilteringMode;
// Shadow data
int NumShadowMatrices;
float4x4 TranslatedWorldToShadow[6];
float4x4 ShadowToTranslatedWorld[6];
float3 TranslatedWorldOrigin;
int2 ShadowResolution;
int MaxSampleCount;
float AbsoluteErrorThreshold;
float RelativeErrorThreshold;
// Dispatch data
int3 GroupCount;
// Debug data
int ShadowDebugTweak;
// Output
RWBuffer<int> RWVolumetricShadowLinkedListAllocatorBuffer;
RWStructuredBuffer<uint2> RWVolumetricShadowLinkedListBuffer;
RWTexture2D<float4> RWBeerShadowMapTexture;
FPrimitiveSceneData GetPrimitiveData(FMaterialVertexParameters Parameters)
{
return GetPrimitiveData(Parameters, LocalToWorld, WorldToLocal, LocalBoundsOrigin, LocalBoundsExtent);
}
FPrimitiveSceneData GetPrimitiveData(FMaterialPixelParameters Parameters)
{
return GetPrimitiveData(Parameters, LocalToWorld, WorldToLocal, LocalBoundsOrigin, LocalBoundsExtent);
}
float4x4 GetLocalToWorld()
{
return LocalToWorld;
}
float4x4 GetWorldToLocal()
{
return WorldToLocal;
}
float3 GetLocalBoundsOrigin()
{
return LocalBoundsOrigin;
}
float3 GetLocalBoundsExtent()
{
return LocalBoundsExtent;
}
int3 GetVolumeResolution()
{
return VoxelResolution;
}
float GetShadowStepSize()
{
return ShadowStepSize;
}
float GetShadowStepFactor()
{
return ShadowStepFactor;
}
float GetStepSize()
{
return GetShadowStepSize();
}
float GetStepFactor()
{
return GetShadowStepFactor();
}
float GetMaxTraceDistance()
{
return MaxTraceDistance;
}
float GetMaxShadowTraceDistance()
{
return MaxShadowTraceDistance;
}
int GetStochasticFilteringMode()
{
return StochasticFilteringMode;
}
float EvalAmbientOcclusion(float3 WorldPosition)
{
return 0.0;
}
float GetIndirectInscatteringFactor()
{
return 0.0;
}
struct FVolumeSampleContext
{
FMaterialPixelParameters MaterialParameters;
FPixelMaterialInputs PixelMaterialInputs;
};
FVolumeSampleContext CreateVolumeSampleContext(
float3 LocalPosition,
float3 WorldPosition,
float3 WorldPosition_DDX,
float3 WorldPosition_DDY,
float MipLevel
)
{
FVolumeSampleContext VolumeSampleContext;
VolumeSampleContext.MaterialParameters = MakeInitializedMaterialPixelParameters();
//VolumeSampleContext.MaterialParameters.PrimitiveId = PrimitiveId;
VolumeSampleContext.MaterialParameters.AbsoluteWorldPosition = DFPromote(WorldPosition);
VolumeSampleContext.MaterialParameters.LWCData.AbsoluteWorldPosition = WSPromote(WorldPosition);
// TODO: Add object centroid to LWC.ObjectWorldPosition
VolumeSampleContext.MaterialParameters.LWCData.LocalToWorld = WSPromote(GetLocalToWorld());
VolumeSampleContext.MaterialParameters.LWCData.WorldToLocal = WSPromoteInverse(GetWorldToLocal());
#if USE_ANALYTIC_DERIVATIVES
VolumeSampleContext.MaterialParameters.WorldPosition_DDX = WorldPosition_DDX;
VolumeSampleContext.MaterialParameters.WorldPosition_DDY = WorldPosition_DDY;
CalcPixelMaterialInputsAnalyticDerivatives(VolumeSampleContext.MaterialParameters, VolumeSampleContext.PixelMaterialInputs);
#else
CalcPixelMaterialInputs(VolumeSampleContext.MaterialParameters, VolumeSampleContext.PixelMaterialInputs);
#endif
return VolumeSampleContext;
}
FVolumeSampleContext CreateVolumeSampleContext(float3 LocalPosition, float3 WorldPosition, float MipLevel)
{
float3 WorldPosition_DDX = 0.0;
float3 WorldPosition_DDY = 0.0;
return CreateVolumeSampleContext(
LocalPosition,
WorldPosition,
WorldPosition_DDX,
WorldPosition_DDY,
MipLevel
);
}
FVolumeSampleContext CreateVolumeSampleContext(float3 LocalPosition, float3 WorldPosition, float3 FilterWidth, float MipLevel, float Rand, int StochasticFilteringMode)
{
// TODO: Use FilterWidth determine MipLevel
//float3 WorldPosition_DDX = mul(float4(LocalPosition + float3(1.0, 0.0, 0.0), 1.0), GetLocalToWorld()).xyz;
//float3 WorldPosition_DDY = mul(float4(LocalPosition + float3(0.0, 1.0, 0.0), 1.0), GetLocalToWorld()).xyz;
//float3 WorldPosition_DDZ = mul(float4(LocalPosition + float3(0.0, 0.0, 1.0), 1.0), GetLocalToWorld()).xyz;
if (StochasticFilteringMode == STOCHASTIC_FILTERING_CONSTANT)
{
LocalPosition = StochasticFilteringConstant(LocalPosition, Rand);
}
else if (StochasticFilteringMode == STOCHASTIC_FILTERING_TRILINEAR)
{
LocalPosition = StochasticFilteringTrilinear(LocalPosition, Rand);
}
else if (StochasticFilteringMode == STOCHASTIC_FILTERING_TRICUBIC)
{
LocalPosition = StochasticFilteringTricubic(LocalPosition, Rand);
}
WorldPosition = mul(float4(LocalPosition, 1.0), GetLocalToWorld()).xyz;
return CreateVolumeSampleContext(LocalPosition, WorldPosition, MipLevel);
}
float3 SampleExtinction(inout FVolumeSampleContext Context)
{
float3 Extinction = SampleExtinctionCoefficients(Context.PixelMaterialInputs);
return Extinction;
}
float3 SampleEmission(inout FVolumeSampleContext Context)
{
float3 Emission = SampleEmissive(Context.PixelMaterialInputs);
return Emission;
}
float3 SampleAlbedo(inout FVolumeSampleContext Context)
{
float3 Albedo = SampleAlbedo(Context.PixelMaterialInputs);
return Albedo;
}
uint GetLinearIndex(uint Face, uint2 PixelCoord)
{
uint LinearIndex = Face * ShadowResolution.x * ShadowResolution.y + PixelCoord.y * ShadowResolution.x + PixelCoord.x;
return LinearIndex;
}
// Cinematic feature options
bool UseInscatteringVolume()
{
return true;
}
#define INDIRECT_LIGHTING_MODE_OFF 0
#define INDIRECT_LIGHTING_MODE_LIGHTING_CACHE 1
#define INDIRECT_LIGHTING_MODE_SINGLE_SCATTERING 2
int GetIndirectLightingMode()
{
return INDIRECT_LIGHTING_MODE_OFF;
}
bool IsOfflineRender()
{
return false;
}
int ShouldApplyHeightFog()
{
return false;
}
int ShouldApplyVolumetricFog()
{
return false;
}
#define FOG_INSCATTERING_MODE_OFF 0
#define FOG_INSCATTERING_MODE_REFERENCE 1
#define FOG_INSCATTERING_MODE_LINEAR_APPROX 2
int GetFogInscatteringMode()
{
return FOG_INSCATTERING_MODE_OFF;
}
#include "HeterogeneousVolumesRayMarchingUtils.ush"
int FindSampleIndexWithLowestError(
int PixelIndex
)
{
// Find sample, whose removal would mark the lowest overall geometric error
int LowestErrorIndex = 0;
float LowestError = 1.0e6;
for (int Index = 1; Index < MaxSampleCount - 1; ++Index)
{
int PrevIndex = AVSM_GetLinkedListPrevPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + Index]);
int NextIndex = AVSM_GetLinkedListNextPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + Index]);
FAVSMSampleData Data[3] = {
AVSM_GetLinkedListSampleData(RWVolumetricShadowLinkedListBuffer[PixelIndex + PrevIndex]),
AVSM_GetLinkedListSampleData(RWVolumetricShadowLinkedListBuffer[PixelIndex + Index]),
AVSM_GetLinkedListSampleData(RWVolumetricShadowLinkedListBuffer[PixelIndex + NextIndex])
};
float Error = abs(
(Data[2].X - Data[0].X) * (Data[2].Tau - Data[1].Tau) -
(Data[2].X - Data[1].X) * (Data[2].Tau - Data[0].Tau)
);
if (Error < LowestError)
{
LowestError = Error;
LowestErrorIndex = Index;
}
}
return LowestErrorIndex;
}
void AddSample(FAVSMSampleData SampleData, int PixelIndex, inout int PrevSampleIndex, inout int SampleIndex)
{
AVSM_SetLinkedListSampleData(RWVolumetricShadowLinkedListBuffer[PixelIndex + SampleIndex], SampleData);
AVSM_SetLinkedListPrevPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + SampleIndex], PrevSampleIndex);
AVSM_SetLinkedListNextPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + SampleIndex], SampleIndex + 1);
PrevSampleIndex = SampleIndex;
SampleIndex = SampleIndex + 1;
}
void ReplaceSample(
int PixelIndex,
int SampleIndex,
FAVSMSampleData SampleData,
int PrevPtrIndex,
int NextPtrIndex
)
{
// Remove
{
int PrevIndex = AVSM_GetLinkedListPrevPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + SampleIndex]);
int NextIndex = AVSM_GetLinkedListNextPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + SampleIndex]);
AVSM_SetLinkedListNextPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + PrevIndex], NextIndex);
AVSM_SetLinkedListPrevPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + NextIndex], PrevIndex);
}
// Replace
AVSM_SetLinkedListSampleData(RWVolumetricShadowLinkedListBuffer[PixelIndex + SampleIndex], SampleData);
AVSM_SetLinkedListPrevPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + SampleIndex], PrevPtrIndex);
AVSM_SetLinkedListNextPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + SampleIndex], NextPtrIndex);
// Update neighborhood
AVSM_SetLinkedListPrevPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + NextPtrIndex], SampleIndex);
AVSM_SetLinkedListNextPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + PrevPtrIndex], SampleIndex);
}
void AddOrReplaceSample(FAVSMSampleData Data, int PixelIndex, inout int PrevSampleIndex, inout int SampleIndex)
{
// Add
if (SampleIndex < MaxSampleCount - 1)
{
AddSample(Data, PixelIndex, PrevSampleIndex, SampleIndex);
}
// Or replace
else
{
#if USE_AVSM_COMPRESSION
// Insert fictious sample at MaxSampleCount - 1 to compute error
AVSM_SetLinkedListSampleData(RWVolumetricShadowLinkedListBuffer[PixelIndex + MaxSampleCount - 1], Data);
AVSM_SetLinkedListNextPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + PrevSampleIndex], MaxSampleCount - 1);
AVSM_SetLinkedListPrevPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + MaxSampleCount - 1], PrevSampleIndex);
int LowestErrorIndex = FindSampleIndexWithLowestError(PixelIndex);
if (LowestErrorIndex > 0)
{
if (LowestErrorIndex == PrevSampleIndex)
{
AVSM_SetLinkedListSampleData(RWVolumetricShadowLinkedListBuffer[PixelIndex + PrevSampleIndex], Data);
}
else
{
ReplaceSample(PixelIndex, LowestErrorIndex, Data, PrevSampleIndex, MaxSampleCount - 1);
}
PrevSampleIndex = LowestErrorIndex;
}
#else
AVSM_SetLinkedListSampleData(RWVolumetricShadowLinkedListBuffer[PixelIndex + MaxSampleCount - 1], Data);
AVSM_SetLinkedListPrevPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + MaxSampleCount - 1], PrevSampleIndex);
PrevSampleIndex = MaxSampleCount - 1;
#endif
}
}
int CameraDownsampleFactor;
[numthreads(THREADGROUP_SIZE_2D, THREADGROUP_SIZE_2D, 1)]
void RenderVolumetricShadowMapForLightWithLiveShadingCS(
uint2 GroupThreadId : SV_GroupThreadID,
uint3 DispatchThreadId : SV_DispatchThreadID
)
{
// Create screen ray
if (any(DispatchThreadId.xy >= ShadowResolution))
{
return;
}
uint Face = DispatchThreadId.z;
uint2 PixelCoord = DispatchThreadId.xy;
uint LinearIndex = GetLinearIndex(Face, PixelCoord);
int PixelIndex = LinearIndex * MaxSampleCount;
// Generate a light ray
float2 ScreenJitter = IsOfflineRender() ? BlueNoiseVec2(PixelCoord, View.StateFrameIndex) : View.TemporalAAJitter.xy;
//float2 ShadowUV = float2(PixelCoord + 0.5f) / float2(ShadowResolution);
float2 ShadowUV = float2(PixelCoord + ScreenJitter) / float2(ShadowResolution);
float WorldRayLength = GetMaxTraceDistance();
#if USE_CAMERA_SCENE_DEPTH
float2 ViewPixelCoord = (PixelCoord + View.TemporalAAJitter.xy) * CameraDownsampleFactor + View.ViewRectMin.xy / CameraDownsampleFactor;
float DeviceZ = SceneDepthTexture.Load(int3(ViewPixelCoord, 0)).r;
#if HAS_INVERTED_Z_BUFFER
DeviceZ = max(0.000000000001, DeviceZ);
#endif // HAS_INVERTED_Z_BUFFER
// Project ray through view transform to trim ray-length by opaque depth
FDFVector3 WorldViewRayOrigin = DFFastSubtract(GetTranslatedWorldCameraPosFromView(ViewPixelCoord), PrimaryView.PreViewTranslation);
FDFVector3 WorldViewRayEnd = SvPositionToWorld(float4(ViewPixelCoord, DeviceZ, 1));
float3 WorldViewRayDirection = DFFastSubtractDemote(WorldViewRayEnd, WorldViewRayOrigin);
float WorldViewRayLength = length(WorldViewRayDirection);
WorldRayLength = min(WorldRayLength, WorldViewRayLength);
#endif
const float NearScreenZ = 1.0;
const float FarScreenZ = 0.0;
float4 LightRayStartAsFloat4 = mul(float4(ShadowUV, NearScreenZ, 1), ShadowToTranslatedWorld[Face]);
float4 LightRayEndAsFloat4 = mul(float4(ShadowUV, FarScreenZ, 1), ShadowToTranslatedWorld[Face]);
float3 LightRayStart = LightRayStartAsFloat4.xyz * rcp(LightRayStartAsFloat4.w);
float3 LightRayEnd = LightRayEndAsFloat4.xyz * rcp(LightRayEndAsFloat4.w);
float3 WorldRayOrigin = DFFastSubtractDemote(LightRayStart, PrimaryView.PreViewTranslation);
float3 WorldRayEnd = DFFastSubtractDemote(LightRayEnd, PrimaryView.PreViewTranslation);
float3 WorldRayDirection = normalize(WorldRayEnd - WorldRayOrigin);
WorldRayEnd = WorldRayOrigin + WorldRayDirection * WorldRayLength;
float3 LocalRayOrigin = mul(float4(WorldRayOrigin, 1.0), WorldToLocal).xyz;
float3 LocalRayEnd = mul(float4(WorldRayEnd, 1.0), WorldToLocal).xyz;
float3 LocalRayDirection = LocalRayEnd - LocalRayOrigin;
float LocalRayLength = length(LocalRayDirection);
LocalRayDirection /= LocalRayLength;
float3 LocalBoundsMin = LocalBoundsOrigin - LocalBoundsExtent;
float3 LocalBoundsMax = LocalBoundsOrigin + LocalBoundsExtent;
float3 Transmittance = 1;
// Intersect bounding volume
float2 HitT = IntersectAABB(LocalRayOrigin, LocalRayDirection, 0.0, LocalRayLength, LocalBoundsMin, LocalBoundsMax);
float HitSpan = HitT.y - HitT.x;
if (HitSpan > 0.0)
{
float Jitter = bJitter ? BlueNoiseScalar(PixelCoord, View.StateFrameIndexMod8) : 0.0;
int bApplyEmissionAndTransmittance = 0;
int bApplyDirectLighting = 0;
int bApplyShadowTransmittance = 0;
FRayMarchingContext RayMarchingContext = CreateRayMarchingContext(
// Local-space
LocalRayOrigin,
LocalRayDirection,
HitT.x,
HitT.y,
// World-space
WorldRayOrigin,
WorldRayDirection,
// Ray-step attributes
Jitter,
CalcShadowStepSize(WorldRayDirection),
MaxStepCount,
//MaxSampleCount, // Replace max-steps with max-samples..
bApplyEmissionAndTransmittance,
bApplyDirectLighting,
bApplyShadowTransmittance
);
RandomSequence RandSequence;
RandomSequence_Initialize(RandSequence, LinearIndex, View.StateFrameIndex);
RayMarchingContext.RandSequence = RandSequence;
uint StepCount = CalcStepCount(RayMarchingContext);
float3 ExpectedExtinction = 0.0;
float3 ExpectedTransmittance = 1.0;
int PrevElementIndex = AVSM_NULL_PTR;
int ElementIndex = 0;
float StepSize = RayMarchingContext.StepSize;
float JitterSize = StepSize * RayMarchingContext.Jitter;
float LocalHitT = HitT.x;
float WorldHitT = LocalHitT * RayMarchingContext.LocalToWorldScale;
float LastWorldHitT = WorldHitT;
for (uint StepIndex = 0; StepIndex < StepCount; ++StepIndex)
{
LocalHitT = min(RayMarchingContext.LocalRayTMin + StepSize * (RayMarchingContext.Jitter + StepIndex), RayMarchingContext.LocalRayTMax);
if (StepIndex == StepCount - 1)
{
float UnjitteredLocalHitT = LocalHitT - JitterSize;
StepSize = RayMarchingContext.LocalRayTMax - UnjitteredLocalHitT;
}
WorldHitT = LocalHitT * RayMarchingContext.LocalToWorldScale;
float3 LocalPosition = RayMarchingContext.LocalRayOrigin + RayMarchingContext.LocalRayDirection * LocalHitT;
float3 WorldPosition = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection * WorldHitT;
FVolumeSampleContext SampleContext = CreateVolumeSampleContext(LocalPosition, WorldPosition, RayMarchingContext.MipLevel);
float3 Extinction = SampleExtinction(SampleContext);
Transmittance *= exp(-Extinction * StepSize);
if (any(Extinction > 0.0))
{
LastWorldHitT = WorldHitT;
bool bFirstEntry = (ElementIndex == 0);
if (bFirstEntry)
{
FAVSMSampleData SampleData = AVSM_CreateSampleData(WorldHitT, Transmittance);
AddSample(SampleData, PixelIndex, PrevElementIndex, ElementIndex);
// Set expected values
ExpectedExtinction = Extinction;
ExpectedTransmittance = Transmittance;
}
else
{
// If extrapolated transmittance is beyond relative error tolerance
ExpectedTransmittance *= exp(-ExpectedExtinction * StepSize);
float AbsoluteError = length(Transmittance - ExpectedTransmittance);
float RelativeError = length((Transmittance - ExpectedTransmittance) / ExpectedTransmittance);
if ((AbsoluteError > AbsoluteErrorThreshold) && (RelativeError > RelativeErrorThreshold))
{
FAVSMSampleData SampleData = AVSM_CreateSampleData(WorldHitT, Transmittance);
AddOrReplaceSample(SampleData, PixelIndex, PrevElementIndex, ElementIndex);
// Reset expected values
ExpectedExtinction = Extinction;
ExpectedTransmittance = Transmittance;
}
}
}
// Early termination
float Epsilon = 1.0e-7;
if (all(Transmittance < Epsilon))
{
Transmittance = 0.0;
break;
}
}
// Stitch up the previous entry
if (PrevElementIndex != AVSM_NULL_PTR)
{
AVSM_SetLinkedListNextPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + PrevElementIndex], ElementIndex);
}
// Last Entry
AVSM_SetLinkedListSampleData(RWVolumetricShadowLinkedListBuffer[PixelIndex + ElementIndex], AVSM_CreateSampleData(LastWorldHitT, Transmittance));
AVSM_SetLinkedListPrevPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + ElementIndex], PrevElementIndex);
AVSM_SetLinkedListNextPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex + ElementIndex], AVSM_NULL_PTR);
}
else
{
AVSM_SetLinkedListSampleData(RWVolumetricShadowLinkedListBuffer[PixelIndex], AVSM_CreateSampleData(0.0, Transmittance));
AVSM_SetLinkedListPrevPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex], AVSM_NULL_PTR);
AVSM_SetLinkedListNextPtr(RWVolumetricShadowLinkedListBuffer[PixelIndex], AVSM_NULL_PTR);
}
if (Face == ShadowDebugTweak)
{
RWBeerShadowMapTexture[PixelCoord] = float4(Transmittance, 1);
}
}