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

361 lines
16 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ShadowProjectionCommon.usf: Contains functions that uniformly filter a depth buffer.
=============================================================================*/
Texture2D ShadowDepthTexture;
SamplerState ShadowDepthTextureSampler;
// xy:unused, z:SoftTransitionScale
float3 SoftTransitionScale;
// xy:ShadowTexelSize.xy, zw:1/ShadowTexelSize.xy
float4 ShadowBufferSize;
/** Cube map texture. */
TextureCube ShadowDepthCubeTexture;
// This is required on GLSL based languages as the standard does NOT allow having a texture being used with both a Comparison and regular SamplerState
#define USE_SEPARATE_SHADOW_DEPTH_CUBE_TEXTURE (OPENGL_PROFILE || COMPILER_VULKAN || PLATFORM_NEEDS_SEPARATE_SHADOW_DEPTH_CUBE_TEXTURE)
#if USE_SEPARATE_SHADOW_DEPTH_CUBE_TEXTURE
TextureCube ShadowDepthCubeTexture2;
#endif
/** Sampler state used for hardware PCF. */
SamplerComparisonState ShadowDepthCubeTextureSampler;
/** View projection matrices that were used in the shadow depth pass for each cube face. */
float4x4 ShadowViewProjectionMatrices[6];
float InvShadowmapResolution;
// @param SideVector needs to be scale with InvShadowmapResolution
// @param UpVector needs to be scale with InvShadowmapResolution
float CubePCF3x3Quarter(float3 NormalizedLightVector, float3 SideVector, float3 UpVector, float CompareDistance, float Bias = 0)
{
float3 Sample00Coordinate = NormalizedLightVector + SideVector * 0.5f + UpVector * 0.5f;
float3 Sample01Coordinate = NormalizedLightVector + SideVector * 0.5f + UpVector * 1.5f;
float3 Sample02Coordinate = NormalizedLightVector + SideVector * 0.5f + UpVector * 2.5f;
float3 Sample10Coordinate = NormalizedLightVector + SideVector * 1.5f + UpVector * 0.5f;
float3 Sample11Coordinate = NormalizedLightVector + SideVector * 1.5f + UpVector * 1.5f;
float3 Sample12Coordinate = NormalizedLightVector + SideVector * 1.5f + UpVector * 2.5f;
float3 Sample20Coordinate = NormalizedLightVector + SideVector * 2.5f + UpVector * 0.5f;
float3 Sample21Coordinate = NormalizedLightVector + SideVector * 2.5f + UpVector * 1.5f;
float3 Sample22Coordinate = NormalizedLightVector + SideVector * 2.5f + UpVector * 2.5f;
// Lookup and combine 9 hardware PCF lookups
float3 ShadowResults0;
ShadowResults0.x = ShadowDepthCubeTexture.SampleCmpLevelZero(ShadowDepthCubeTextureSampler, Sample00Coordinate, CompareDistance + Bias * length(float2(0.5f, 0.5f)));
ShadowResults0.y = ShadowDepthCubeTexture.SampleCmpLevelZero(ShadowDepthCubeTextureSampler, Sample01Coordinate, CompareDistance + Bias * length(float2(0.5f, 1.5f)));
ShadowResults0.z = ShadowDepthCubeTexture.SampleCmpLevelZero(ShadowDepthCubeTextureSampler, Sample02Coordinate, CompareDistance + Bias * length(float2(0.5f, 2.5f)));
float3 ShadowResults1;
ShadowResults1.x = ShadowDepthCubeTexture.SampleCmpLevelZero(ShadowDepthCubeTextureSampler, Sample10Coordinate, CompareDistance + Bias * length(float2(1.5f, 0.5f)));
ShadowResults1.y = ShadowDepthCubeTexture.SampleCmpLevelZero(ShadowDepthCubeTextureSampler, Sample11Coordinate, CompareDistance + Bias * length(float2(1.5f, 1.5f)));
ShadowResults1.z = ShadowDepthCubeTexture.SampleCmpLevelZero(ShadowDepthCubeTextureSampler, Sample12Coordinate, CompareDistance + Bias * length(float2(1.5f, 2.5f)));
float3 ShadowResults2;
ShadowResults2.x = ShadowDepthCubeTexture.SampleCmpLevelZero(ShadowDepthCubeTextureSampler, Sample20Coordinate, CompareDistance + Bias * length(float2(2.5f, 0.5f)));
ShadowResults2.y = ShadowDepthCubeTexture.SampleCmpLevelZero(ShadowDepthCubeTextureSampler, Sample21Coordinate, CompareDistance + Bias * length(float2(2.5f, 1.5f)));
ShadowResults2.z = ShadowDepthCubeTexture.SampleCmpLevelZero(ShadowDepthCubeTextureSampler, Sample22Coordinate, CompareDistance + Bias * length(float2(2.5f, 2.5f)));
return dot(ShadowResults0 + ShadowResults1 + ShadowResults2, .1111111f);
}
static const float2 DiscSamples5[]=
{ // 5 random points in disc with radius 2.500000
float2(0.000000, 2.500000),
float2(2.377641, 0.772542),
float2(1.469463, -2.022543),
float2(-1.469463, -2.022542),
float2(-2.377641, 0.772543),
};
static const float2 DiscSamples12[]=
{ // 12 random points in disc with radius 2.500000
float2(0.000000, 2.500000),
float2(1.767767, 1.767767),
float2(2.500000, -0.000000),
float2(1.767767, -1.767767),
float2(-0.000000, -2.500000),
float2(-1.767767, -1.767767),
float2(-2.500000, 0.000000),
float2(-1.767766, 1.767768),
float2(-1.006119, -0.396207),
float2(1.000015, 0.427335),
float2(0.416807, -1.006577),
float2(-0.408872, 1.024430),
};
static const float2 DiscSamples29[]=
{ // 29 random points in disc with radius 2.500000
float2(0.000000, 2.500000),
float2(1.016842, 2.283864),
float2(1.857862, 1.672826),
float2(2.377641, 0.772542),
float2(2.486305, -0.261321),
float2(2.165063, -1.250000),
float2(1.469463, -2.022543),
float2(0.519779, -2.445369),
float2(-0.519779, -2.445369),
float2(-1.469463, -2.022542),
float2(-2.165064, -1.250000),
float2(-2.486305, -0.261321),
float2(-2.377641, 0.772543),
float2(-1.857862, 1.672827),
float2(-1.016841, 2.283864),
float2(0.091021, -0.642186),
float2(0.698035, 0.100940),
float2(0.959731, -1.169393),
float2(-1.053880, 1.180380),
float2(-1.479156, -0.606937),
float2(-0.839488, -1.320002),
float2(1.438566, 0.705359),
float2(0.067064, -1.605197),
float2(0.728706, 1.344722),
float2(1.521424, -0.380184),
float2(-0.199515, 1.590091),
float2(-1.524323, 0.364010),
float2(-0.692694, -0.086749),
float2(-0.082476, 0.654088),
};
void CalcCubemapVectorAndFaceIndex(float3 NormalizedLightVector, float ShadowFilterRadius, out float3 SideVector, out float3 UpVector, out int CubeFaceIndex)
{
SideVector = normalize(cross(NormalizedLightVector, float3(0, 0, 1)));
UpVector = cross(SideVector, NormalizedLightVector);
SideVector *= InvShadowmapResolution * ShadowFilterRadius;
UpVector *= InvShadowmapResolution * ShadowFilterRadius;
// TODO: this should use GCN intrinsic when available
// Figure out which cube face we're sampling from
float3 AbsLightVector = abs(NormalizedLightVector);
float MaxCoordinate = max(AbsLightVector.x, max(AbsLightVector.y, AbsLightVector.z));
if (MaxCoordinate == AbsLightVector.x)
{
CubeFaceIndex = AbsLightVector.x == NormalizedLightVector.x ? 0 : 1;
}
else if (MaxCoordinate == AbsLightVector.y)
{
CubeFaceIndex = AbsLightVector.y == NormalizedLightVector.y ? 2 : 3;
}
else
{
CubeFaceIndex = AbsLightVector.z == NormalizedLightVector.z ? 4 : 5;
}
}
/** Computes shadowing for a given world position from a cubemap shadowmap used on a point light. */
float CubemapHardwarePCF(
TextureCube InShadowDepthCubeTexture, SamplerComparisonState InShadowDepthCubeTextureSampler, float4x4 InShadowViewProjectionMatrices[6], float InInvShadowmapResolution,
float3 WorldPosition, float3 LightPosition, float LightInvRadius, float DepthBias, float SlopeDepthBias, float MaxSlopeDepthBias)
{
float Shadow = 1;
float3 WorldSampleToLightVec = LightPosition - WorldPosition.xyz;
float Distance = length(WorldSampleToLightVec);
BRANCH
// Skip pixels outside of the light's influence
if (Distance * LightInvRadius < 1.0f)
{
float3 NormalizedLightVector = WorldSampleToLightVec / Distance;
float3 SideVector = normalize(cross(NormalizedLightVector, float3(0, 0, 1)));
float3 UpVector = cross(SideVector, NormalizedLightVector);
SideVector *= InvShadowmapResolution;
UpVector *= InvShadowmapResolution;
// Figure out which cube face we're sampling from
float3 AbsLightVector = abs(WorldSampleToLightVec);
float MaxCoordinate = max(AbsLightVector.x, max(AbsLightVector.y, AbsLightVector.z));
int CubeFaceIndex = 0;
if (MaxCoordinate == AbsLightVector.x)
{
CubeFaceIndex = AbsLightVector.x == WorldSampleToLightVec.x ? 0 : 1;
}
else if (MaxCoordinate == AbsLightVector.y)
{
CubeFaceIndex = AbsLightVector.y == WorldSampleToLightVec.y ? 2 : 3;
}
else
{
CubeFaceIndex = AbsLightVector.z == WorldSampleToLightVec.z ? 4 : 5;
}
// Transform the Light-relative position into shadow space (The light shadow view is pre-translated wrt the main view)
float4 ShadowPosition = mul(float4(-WorldSampleToLightVec, 1), InShadowViewProjectionMatrices[CubeFaceIndex]);
// Calculate the Z buffer value that would have been stored for this position in the shadow map
float CompareDistance = ShadowPosition.z / ShadowPosition.w;
float ShadowDepthBias = - (DepthBias + SlopeDepthBias) / ShadowPosition.w;
Shadow = 0;
#if SHADOW_QUALITY <= 2
Shadow = InShadowDepthCubeTexture.SampleCmpLevelZero(InShadowDepthCubeTextureSampler, WorldSampleToLightVec, CompareDistance - ShadowDepthBias);
#elif SHADOW_QUALITY == 3
UNROLL for(int i = 0; i < 5; ++i)
{
float3 SamplePos = NormalizedLightVector + SideVector * DiscSamples5[i].x + UpVector * DiscSamples5[i].y;
Shadow += InShadowDepthCubeTexture.SampleCmpLevelZero(
InShadowDepthCubeTextureSampler,
SamplePos,
CompareDistance - ShadowDepthBias * length(DiscSamples5[i]));
}
Shadow /= 5;
#elif SHADOW_QUALITY == 4
UNROLL for(int i = 0; i < 12; ++i)
{
float3 SamplePos = NormalizedLightVector + SideVector * DiscSamples12[i].x + UpVector * DiscSamples12[i].y;
Shadow += InShadowDepthCubeTexture.SampleCmpLevelZero(
InShadowDepthCubeTextureSampler,
SamplePos,
CompareDistance - ShadowDepthBias * length(DiscSamples12[i]));
}
Shadow /= 12;
#else // SHADOW_QUALITY
UNROLL for(int i = 0; i < 29; ++i)
{
float3 SamplePos = NormalizedLightVector + SideVector * DiscSamples29[i].x + UpVector * DiscSamples29[i].y;
Shadow += InShadowDepthCubeTexture.SampleCmpLevelZero(
InShadowDepthCubeTextureSampler,
SamplePos,
CompareDistance - ShadowDepthBias * length(DiscSamples29[i]));
}
Shadow /= 29;
#endif // SHADOW_QUALITY
/*// 4 * 9 samples (former non randomized sample pattern, more samples, less ALU, slower)
{
float4 ShadowResults;
// TODO CubePCF3x3Quarter should use the In parameters similarly to InShadowDepthCubeTexture
ShadowResults.x = CubePCF3x3Quarter(NormalizedLightVector, - SideVector, - UpVector, CompareDistance, ShadowDepthBias);
ShadowResults.y = CubePCF3x3Quarter(NormalizedLightVector, - SideVector, UpVector, CompareDistance, ShadowDepthBias);
ShadowResults.z = CubePCF3x3Quarter(NormalizedLightVector, SideVector, - UpVector, CompareDistance, ShadowDepthBias);
ShadowResults.w = CubePCF3x3Quarter(NormalizedLightVector, SideVector, UpVector, CompareDistance, ShadowDepthBias);
Shadow = dot(ShadowResults, .25f);
}*/
}
// ReverseZ so flip the result
return 1.0f - Shadow;
}
float CubemapHardwarePCF(
float3 WorldPosition, float3 LightPosition, float LightInvRadius, float DepthBias, float SlopeDepthBias, float MaxSlopeDepthBias)
{
return CubemapHardwarePCF(ShadowDepthCubeTexture, ShadowDepthCubeTextureSampler, ShadowViewProjectionMatrices, InvShadowmapResolution,
WorldPosition, LightPosition, LightInvRadius, DepthBias, SlopeDepthBias, MaxSlopeDepthBias);
}
#ifndef NO_TRANSLUCENCY_AVAILABLE
/** Textures and parameters needed for Fourier opacity maps. */
#define TranslucencyShadowTransmission0 TranslucentSelfShadow.Transmission0
#define TranslucencyShadowTransmission1 TranslucentSelfShadow.Transmission1
#if SUPPORTS_INDEPENDENT_SAMPLERS
#define TranslucencyShadowTransmission0Sampler View.SharedBilinearClampedSampler
#define TranslucencyShadowTransmission1Sampler View.SharedBilinearClampedSampler
#else
#define TranslucencyShadowTransmission0Sampler TranslucentSelfShadow.Transmission0Sampler
#define TranslucencyShadowTransmission1Sampler TranslucentSelfShadow.Transmission1Sampler
#endif
/** These have been tweaked to minimize ringing while not losing too much high frequency in the captured opacity function. */
static const float RingingSuppressionFactor = 1;
static const float NumTermsForRingingSuppression = 8;
// Creates a scale for a given frequency that minimizes ringing inherent with a Fourier basis
// This is done by scaling down the contribution of the higher frequency components
// Using a macro to support any size vector
#define RingingSuppressionScale(k) (exp(-RingingSuppressionFactor * Square(k / NumTermsForRingingSuppression)))
float CalculateTranslucencyShadowingDensity(float2 ShadowUV, float ShadingDepth)
{
// Needs to match the corresponding define in TranslucentShadowDepthShaders.usf
#define USE_FOURIER_OPACITY_MAP 1
#if USE_FOURIER_OPACITY_MAP
// Fourier opacity shadow map
// Fetch the accumulated Fourier coefficients generated by rendering all translucent casters
float4 CosCoefficients0 = Texture2DSampleLevel(TranslucencyShadowTransmission0, TranslucencyShadowTransmission0Sampler, ShadowUV, 0);
float4 SinCoefficients0 = Texture2DSampleLevel(TranslucencyShadowTransmission1, TranslucencyShadowTransmission1Sampler, ShadowUV, 0);
float3 FrequencyScales0 = 2.0 * PI * float3(1, 2, 3);
float3 CoefficientScales0 = RingingSuppressionScale(float3(1, 2, 3)) / FrequencyScales0;
// Calculate the sin and cos wave scales for each frequency based on the depth of the point being shaded
float3 ShadingSinCoefficient0;
float3 ShadingCosCoefficient0;
sincos(FrequencyScales0 * ShadingDepth, ShadingSinCoefficient0, ShadingCosCoefficient0);
ShadingCosCoefficient0 = 1 - ShadingCosCoefficient0;
// Dot the two sets of Fourier coefficients to calculate the density from translucent casters at the shading point along the ray to the light
float FinalDensity = (CosCoefficients0.x * ShadingDepth / 2.0) + dot(ShadingSinCoefficient0 * CosCoefficients0.yzw * CoefficientScales0, 1) + dot(ShadingCosCoefficient0 * SinCoefficients0.yzw * CoefficientScales0, 1);
return FinalDensity;
#else
// Opacity shadow map
float LayerSize = 1.0f / 15.0f;
float4 LayerDepths0 = float4(0, 1, 2, 3) * LayerSize;
float4 LayerDepths1 = float4(4, 5, 6, 7) * LayerSize;
float4 LayerDepths2 = float4(8, 9, 10, 11) * LayerSize;
float4 LayerDepths3 = float4(12, 13, 14, 15) * LayerSize;
float4 Densities0 = Texture2DSampleLevel(TranslucencyShadowTransmission0, TranslucencyShadowTransmission0Sampler, ShadowUV, 0);
float4 Densities1 = Texture2DSampleLevel(TranslucencyShadowTransmission1, TranslucencyShadowTransmission1Sampler, ShadowUV, 0);
float4 Densities2 = Texture2DSampleLevel(TranslucencyShadowTransmission2, TranslucencyShadowTransmission2Sampler, ShadowUV, 0);
float4 Densities3 = Texture2DSampleLevel(TranslucencyShadowTransmission3, TranslucencyShadowTransmission3Sampler, ShadowUV, 0);
// Setup lerp fractions between layers
float4 LayerFractions0 = (ShadingDepth - LayerDepths0) / LayerSize;
float4 LayerFractions1 = (ShadingDepth - LayerDepths1) / LayerSize;
float4 LayerFractions2 = (ShadingDepth - LayerDepths2) / LayerSize;
float4 LayerFractions3 = (ShadingDepth - LayerDepths3) / LayerSize;
// Setup a mask where the active layer has a value of 1, all other layers have 0
// LayerMask0.x has no lower bound, and LayerMask3.w has no upper bound, so that pixels outside the range of the opacity shadow map are handled
float4 LayerMask0 = (LayerFractions0 < 1) * float4(1, LayerFractions0.yzw >= 0);
float4 LayerMask1 = (LayerFractions1 < 1) * (LayerFractions1 >= 0);
float4 LayerMask2 = (LayerFractions2 < 1) * (LayerFractions2 >= 0);
float4 LayerMask3 = float4(LayerFractions3.xyz < 1, 1) * (LayerFractions3 >= 0);
// Lerp between the nearest 2 layer's transmissions, using LayerMask to select layer attributes
float FinalDensity0 = lerp(dot(LayerMask0, float4(0, Densities0.xyz)), dot(LayerMask0, Densities0), saturate(dot(LayerMask0, LayerFractions0)));
float FinalDensity1 = lerp(dot(LayerMask1, float4(Densities0.w, Densities1.xyz)), dot(LayerMask1, Densities1), saturate(dot(LayerMask1, LayerFractions1)));
float FinalDensity2 = lerp(dot(LayerMask2, float4(Densities1.w, Densities2.xyz)), dot(LayerMask2, Densities2), saturate(dot(LayerMask2, LayerFractions2)));
float FinalDensity3 = lerp(dot(LayerMask3, float4(Densities2.w, Densities3.xyz)), dot(LayerMask3, Densities3), saturate(dot(LayerMask3, LayerFractions3)));
float FinalDensity = FinalDensity0 + FinalDensity1 + FinalDensity2 + FinalDensity3;
return FinalDensity;
#endif
}
float CalculateTranslucencyShadowing(float2 ShadowUV, float ShadingDepth)
{
float ShadowDensity = CalculateTranslucencyShadowingDensity(ShadowUV, ShadingDepth);
float FinalTransmission = saturate(exp(-ShadowDensity));
return FinalTransmission;
}
#endif // NO_TRANSLUCENCY_AVAILABLE