361 lines
16 KiB
HLSL
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
|
|
|
|
|