Refactor shadows rendering to use Shadow Map Atlas
This commit is contained in:
@@ -159,7 +159,7 @@ float4 PS_Lighting(AtlasVertexOutput input) : SV_Target
|
||||
float toLightDst = GLOBAL_SDF_WORLD_SIZE;
|
||||
#endif
|
||||
float4 shadowMask = 1;
|
||||
if (Light.CastShadows > 0)
|
||||
if (Light.ShadowsBufferAddress != 0)
|
||||
{
|
||||
float NoL = dot(gBuffer.Normal, L);
|
||||
float shadowBias = 10.0f;
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
|
||||
#include "./Flax/LightingCommon.hlsl"
|
||||
|
||||
ShadowData GetShadow(LightData lightData, GBufferSample gBuffer, float4 shadowMask)
|
||||
ShadowSample GetShadow(LightData lightData, GBufferSample gBuffer, float4 shadowMask)
|
||||
{
|
||||
ShadowData shadow;
|
||||
ShadowSample shadow;
|
||||
shadow.SurfaceShadow = gBuffer.AO * shadowMask.r;
|
||||
shadow.TransmissionShadow = shadowMask.g;
|
||||
return shadow;
|
||||
}
|
||||
|
||||
LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
LightSample StandardShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
{
|
||||
float3 diffuseColor = GetDiffuseColor(gBuffer);
|
||||
float3 H = normalize(V + L);
|
||||
@@ -22,7 +22,7 @@ LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, floa
|
||||
float NoH = saturate(dot(N, H));
|
||||
float VoH = saturate(dot(V, H));
|
||||
|
||||
LightingData lighting;
|
||||
LightSample lighting;
|
||||
lighting.Diffuse = Diffuse_Lambert(diffuseColor);
|
||||
#if LIGHTING_NO_SPECULAR
|
||||
lighting.Specular = 0;
|
||||
@@ -37,9 +37,9 @@ LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, floa
|
||||
return lighting;
|
||||
}
|
||||
|
||||
LightingData SubsurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
LightSample SubsurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
{
|
||||
LightingData lighting = StandardShading(gBuffer, energy, L, V, N);
|
||||
LightSample lighting = StandardShading(gBuffer, energy, L, V, N);
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
// Fake effect of the light going through the material
|
||||
float3 subsurfaceColor = gBuffer.CustomData.rgb;
|
||||
@@ -53,9 +53,9 @@ LightingData SubsurfaceShading(GBufferSample gBuffer, float energy, float3 L, fl
|
||||
return lighting;
|
||||
}
|
||||
|
||||
LightingData FoliageShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
LightSample FoliageShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
{
|
||||
LightingData lighting = StandardShading(gBuffer, energy, L, V, N);
|
||||
LightSample lighting = StandardShading(gBuffer, energy, L, V, N);
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
// Fake effect of the light going through the thin foliage
|
||||
float3 subsurfaceColor = gBuffer.CustomData.rgb;
|
||||
@@ -67,7 +67,7 @@ LightingData FoliageShading(GBufferSample gBuffer, float energy, float3 L, float
|
||||
return lighting;
|
||||
}
|
||||
|
||||
LightingData SurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
LightSample SurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
{
|
||||
switch (gBuffer.ShadingModel)
|
||||
{
|
||||
@@ -79,7 +79,7 @@ LightingData SurfaceShading(GBufferSample gBuffer, float energy, float3 L, float
|
||||
case SHADING_MODEL_FOLIAGE:
|
||||
return FoliageShading(gBuffer, energy, L, V, N);
|
||||
default:
|
||||
return (LightingData)0;
|
||||
return (LightSample)0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
|
||||
float3 toLight = lightData.Direction;
|
||||
|
||||
// Calculate shadow
|
||||
ShadowData shadow = GetShadow(lightData, gBuffer, shadowMask);
|
||||
ShadowSample shadow = GetShadow(lightData, gBuffer, shadowMask);
|
||||
|
||||
// Calculate attenuation
|
||||
if (isRadial)
|
||||
@@ -147,7 +147,7 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
|
||||
float energy = AreaLightSpecular(lightData, gBuffer.Roughness, toLight, L, V, N);
|
||||
|
||||
// Calculate direct lighting
|
||||
LightingData lighting = SurfaceShading(gBuffer, energy, L, V, N);
|
||||
LightSample lighting = SurfaceShading(gBuffer, energy, L, V, N);
|
||||
|
||||
// Calculate final light color
|
||||
float3 surfaceLight = (lighting.Diffuse + lighting.Specular) * shadow.SurfaceShadow;
|
||||
|
||||
@@ -27,26 +27,26 @@ struct LightData
|
||||
float MinRoughness;
|
||||
|
||||
float3 Position;
|
||||
float CastShadows;
|
||||
uint ShadowsBufferAddress;
|
||||
|
||||
float3 Direction;
|
||||
float Radius;
|
||||
|
||||
float FalloffExponent;
|
||||
float InverseSquared;
|
||||
float Dummy0;
|
||||
float RadiusInv;
|
||||
float Dummy0;
|
||||
};
|
||||
|
||||
// Structure that contains information about shadow
|
||||
struct ShadowData
|
||||
// Structure that contains information about shadow sampling result
|
||||
struct ShadowSample
|
||||
{
|
||||
float SurfaceShadow;
|
||||
float TransmissionShadow;
|
||||
};
|
||||
|
||||
// Structure that contains information about direct lighting calculations result
|
||||
struct LightingData
|
||||
struct LightSample
|
||||
{
|
||||
float3 Diffuse;
|
||||
float3 Specular;
|
||||
|
||||
@@ -61,7 +61,7 @@ void PS_Directional(Quad_VS2PS input, out float4 output : SV_Target0)
|
||||
// Sample shadow mask
|
||||
float4 shadowMask = 1;
|
||||
BRANCH
|
||||
if (Light.CastShadows > 0)
|
||||
if (Light.ShadowsBufferAddress != 0)
|
||||
{
|
||||
shadowMask = SAMPLE_RT(Shadow, input.TexCoord);
|
||||
}
|
||||
@@ -98,7 +98,7 @@ void PS_Point(Model_VS2PS input, out float4 output : SV_Target0)
|
||||
// Sample shadow mask
|
||||
float4 shadowMask = 1;
|
||||
BRANCH
|
||||
if (Light.CastShadows > 0)
|
||||
if (Light.ShadowsBufferAddress != 0)
|
||||
{
|
||||
shadowMask = SAMPLE_RT(Shadow, uv);
|
||||
}
|
||||
@@ -140,7 +140,7 @@ void PS_Spot(Model_VS2PS input, out float4 output : SV_Target0)
|
||||
// Sample shadow mask
|
||||
float4 shadowMask = 1;
|
||||
BRANCH
|
||||
if (Light.CastShadows > 0)
|
||||
if (Light.ShadowsBufferAddress != 0)
|
||||
{
|
||||
shadowMask = SAMPLE_RT(Shadow, uv);
|
||||
}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#ifndef __PCF_KERNELS__
|
||||
#define __PCF_KERNELS__
|
||||
|
||||
// Cascades Shadow Mapping
|
||||
|
||||
#if FilterSizeCSM == 2
|
||||
|
||||
#elif FilterSizeCSM == 3
|
||||
|
||||
static const float CSMFilterWeightsSum = 7;
|
||||
static const float CSMFilterWeights[3][3] =
|
||||
{
|
||||
{ 0.5,1.0,0.5 },
|
||||
{ 1.0,1.0,1.0 },
|
||||
{ 0.5,1.0,0.5 }
|
||||
};
|
||||
|
||||
#elif FilterSizeCSM == 5
|
||||
|
||||
static const float CSMFilterWeightsSum = 17;
|
||||
static const float CSMFilterWeights[5][5] =
|
||||
{
|
||||
{ 0.0,0.5,1.0,0.5,0.0 },
|
||||
{ 0.5,1.0,1.0,1.0,0.5 },
|
||||
{ 1.0,1.0,1.0,1.0,1.0 },
|
||||
{ 0.5,1.0,1.0,1.0,0.5 },
|
||||
{ 0.0,0.5,1.0,0.5,0.0 }
|
||||
};
|
||||
|
||||
#elif FilterSizeCSM == 7
|
||||
|
||||
static const float CSMFilterWeightsSum = 33;
|
||||
static const float CSMFilterWeights[7][7] =
|
||||
{
|
||||
{ 0.0,0.0,0.5,1.0,0.5,0.0,0.0 },
|
||||
{ 0.0,1.0,1.0,1.0,1.0,1.0,0.0 },
|
||||
{ 0.5,1.0,1.0,1.0,1.0,1.0,0.5 },
|
||||
{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0 },
|
||||
{ 0.5,1.0,1.0,1.0,1.0,1.0,0.5 },
|
||||
{ 0.0,1.0,1.0,1.0,1.0,1.0,0.0 },
|
||||
{ 0.0,0.0,0.5,1.0,0.5,0.0,0.0 }
|
||||
};
|
||||
|
||||
#elif FilterSizeCSM == 9
|
||||
|
||||
static const float CSMFilterWeightsSum = 53;
|
||||
static const float CSMFilterWeights[9][9] =
|
||||
{
|
||||
{ 0.0,0.0,0.0,0.5,1.0,0.5,0.0,0.0,0.0 },
|
||||
{ 0.0,0.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0 },
|
||||
{ 0.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0 },
|
||||
{ 0.5,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.5 },
|
||||
{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 },
|
||||
{ 0.5,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.5 },
|
||||
{ 0.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0 },
|
||||
{ 0.0,0.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0 },
|
||||
{ 0.0,0.0,0.0,0.5,1.0,0.5,0.0,0.0,0.0 }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// Cube Map Shadows
|
||||
|
||||
#if FilterSizeCube == 5
|
||||
|
||||
// 5 random points in disc with radius 2.5
|
||||
static const float2 PCFDiscSamples[5] =
|
||||
{
|
||||
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),
|
||||
};
|
||||
|
||||
#elif FilterSizeCube == 12
|
||||
|
||||
// 12 random points in disc with radius 2.5
|
||||
static const float2 PCFDiscSamples[12] =
|
||||
{
|
||||
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),
|
||||
};
|
||||
|
||||
#elif FilterSizeCube == 29
|
||||
|
||||
// 29 random points in disc with radius 2.5
|
||||
static const float2 PCFDiscSamples[29] =
|
||||
{
|
||||
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),
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -56,8 +56,6 @@ float4 PS_CopyLinear(Quad_VS2PS input) : SV_Target
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _PS_Clear
|
||||
|
||||
// Pixel Shader for clearing a render target with a solid color
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_Clear(Quad_VS2PS input) : SV_Target
|
||||
@@ -65,4 +63,9 @@ float4 PS_Clear(Quad_VS2PS input) : SV_Target
|
||||
return Color;
|
||||
}
|
||||
|
||||
#endif
|
||||
// Pixel Shader for clearing depth buffer
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float PS_DepthClear(Quad_VS2PS input) : SV_Depth
|
||||
{
|
||||
return Color.r;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
META_CB_BEGIN(0, PerLight)
|
||||
GBufferData GBuffer;
|
||||
LightData Light;
|
||||
LightShadowData LightShadow;
|
||||
float4x4 WVP;
|
||||
float4x4 ViewProjectionMatrix;
|
||||
float2 Dummy0;
|
||||
@@ -18,8 +17,10 @@ float ContactShadowsDistance;
|
||||
float ContactShadowsLength;
|
||||
META_CB_END
|
||||
|
||||
Buffer<float4> ShadowsBuffer : register(t5);
|
||||
Texture2D<float> ShadowMap : register(t6);
|
||||
|
||||
DECLARE_GBUFFERDATA_ACCESS(GBuffer)
|
||||
DECLARE_LIGHTSHADOWDATA_ACCESS(LightShadow);
|
||||
|
||||
#if CONTACT_SHADOWS
|
||||
|
||||
@@ -67,10 +68,6 @@ Model_VS2PS VS_Model(ModelInput_PosOnly input)
|
||||
return output;
|
||||
}
|
||||
|
||||
#ifdef _PS_PointLight
|
||||
|
||||
TextureCube<float> ShadowMapPoint : register(t5);
|
||||
|
||||
// Pixel shader for point light shadow rendering
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=0,CONTACT_SHADOWS=0)
|
||||
@@ -83,9 +80,6 @@ META_PERMUTATION_2(SHADOWS_QUALITY=2,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=3,CONTACT_SHADOWS=1)
|
||||
float4 PS_PointLight(Model_VS2PS input) : SV_Target0
|
||||
{
|
||||
float shadow = 1;
|
||||
float subsurfaceShadow = 1;
|
||||
|
||||
// Obtain texture coordinates corresponding to the current pixel
|
||||
float2 uv = (input.ScreenPos.xy / input.ScreenPos.w) * float2(0.5, -0.5) + float2(0.5, 0.5);
|
||||
|
||||
@@ -94,68 +88,43 @@ float4 PS_PointLight(Model_VS2PS input) : SV_Target0
|
||||
GBufferSample gBuffer = SampleGBuffer(gBufferData, uv);
|
||||
|
||||
// Sample shadow
|
||||
LightShadowData lightShadowData = GetLightShadowData();
|
||||
shadow = SampleShadow(Light, lightShadowData, ShadowMapPoint, gBuffer, subsurfaceShadow);
|
||||
ShadowSample shadow = SamplePointLightShadow(Light, ShadowsBuffer, ShadowMap, gBuffer);
|
||||
|
||||
#if CONTACT_SHADOWS
|
||||
// Calculate screen-space contact shadow
|
||||
shadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, normalize(Light.Position - gBuffer.WorldPos), ContactShadowsLength);
|
||||
shadow.SurfaceShadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, normalize(Light.Position - gBuffer.WorldPos), ContactShadowsLength);
|
||||
#endif
|
||||
|
||||
return float4(shadow, subsurfaceShadow, 1, 1);
|
||||
return GetShadowMask(shadow);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _PS_DirLight
|
||||
|
||||
Texture2DArray ShadowMapDir : register(t5);
|
||||
|
||||
// Pixel shader for directional light shadow rendering
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=0,CSM_BLENDING=0,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=1,CSM_BLENDING=0,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=2,CSM_BLENDING=0,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=3,CSM_BLENDING=0,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=0,CSM_BLENDING=1,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=1,CSM_BLENDING=1,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=2,CSM_BLENDING=1,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=3,CSM_BLENDING=1,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=0,CSM_BLENDING=0,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=1,CSM_BLENDING=0,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=2,CSM_BLENDING=0,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=3,CSM_BLENDING=0,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=0,CSM_BLENDING=1,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=1,CSM_BLENDING=1,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=2,CSM_BLENDING=1,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=3,CSM_BLENDING=1,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=0,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=1,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=2,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=3,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=0,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=1,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=2,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=3,CONTACT_SHADOWS=1)
|
||||
float4 PS_DirLight(Quad_VS2PS input) : SV_Target0
|
||||
{
|
||||
float shadow = 1;
|
||||
float subsurfaceShadow = 1;
|
||||
|
||||
// Sample GBuffer
|
||||
GBufferData gBufferData = GetGBufferData();
|
||||
GBufferSample gBuffer = SampleGBuffer(gBufferData, input.TexCoord);
|
||||
|
||||
// Sample shadow
|
||||
LightShadowData lightShadowData = GetLightShadowData();
|
||||
shadow = SampleShadow(Light, lightShadowData, ShadowMapDir, gBuffer, subsurfaceShadow);
|
||||
ShadowSample shadow = SampleDirectionalLightShadow(Light, ShadowsBuffer, ShadowMap, gBuffer);
|
||||
|
||||
#if CONTACT_SHADOWS
|
||||
// Calculate screen-space contact shadow
|
||||
shadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, Light.Direction, ContactShadowsLength);
|
||||
shadow.SurfaceShadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, Light.Direction, ContactShadowsLength);
|
||||
#endif
|
||||
|
||||
return float4(shadow, subsurfaceShadow, 1, 1);
|
||||
return GetShadowMask(shadow);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _PS_SpotLight
|
||||
|
||||
Texture2D ShadowMapSpot : register(t5);
|
||||
|
||||
// Pixel shader for spot light shadow rendering
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=0,CONTACT_SHADOWS=0)
|
||||
@@ -168,9 +137,6 @@ META_PERMUTATION_2(SHADOWS_QUALITY=2,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=3,CONTACT_SHADOWS=1)
|
||||
float4 PS_SpotLight(Model_VS2PS input) : SV_Target0
|
||||
{
|
||||
float shadow = 1;
|
||||
float subsurfaceShadow = 1;
|
||||
|
||||
// Obtain texture coordinates corresponding to the current pixel
|
||||
float2 uv = (input.ScreenPos.xy / input.ScreenPos.w) * float2(0.5, -0.5) + float2(0.5, 0.5);
|
||||
|
||||
@@ -179,15 +145,12 @@ float4 PS_SpotLight(Model_VS2PS input) : SV_Target0
|
||||
GBufferSample gBuffer = SampleGBuffer(gBufferData, uv);
|
||||
|
||||
// Sample shadow
|
||||
LightShadowData lightShadowData = GetLightShadowData();
|
||||
shadow = SampleShadow(Light, lightShadowData, ShadowMapSpot, gBuffer, subsurfaceShadow);
|
||||
ShadowSample shadow = SampleSpotLightShadow(Light, ShadowsBuffer, ShadowMap, gBuffer);
|
||||
|
||||
#if CONTACT_SHADOWS
|
||||
// Calculate screen-space contact shadow
|
||||
shadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, normalize(Light.Position - gBuffer.WorldPos), ContactShadowsLength);
|
||||
shadow.SurfaceShadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, normalize(Light.Position - gBuffer.WorldPos), ContactShadowsLength);
|
||||
#endif
|
||||
|
||||
return float4(shadow, subsurfaceShadow, 1, 1);
|
||||
return GetShadowMask(shadow);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,32 +12,57 @@
|
||||
#ifndef SHADOWS_QUALITY
|
||||
#define SHADOWS_QUALITY 0
|
||||
#endif
|
||||
#ifndef CSM_BLENDING
|
||||
#define CSM_BLENDING 0
|
||||
#endif
|
||||
|
||||
// Structure that contains information about light
|
||||
struct LightShadowData
|
||||
// Shadow data for the light
|
||||
struct ShadowData
|
||||
{
|
||||
float2 ShadowMapSize;
|
||||
float Sharpness;
|
||||
float Fade;
|
||||
|
||||
float FadeDistance;
|
||||
float NormalOffsetScale;
|
||||
float Bias;
|
||||
float FadeDistance;
|
||||
uint NumCascades;
|
||||
|
||||
uint TilesCount;
|
||||
float4 CascadeSplits;
|
||||
float4x4 ShadowVP[6];
|
||||
};
|
||||
|
||||
#ifdef PLATFORM_ANDROID
|
||||
// #AdrenoVK_CB_STRUCT_MEMBER_ACCESS_BUG
|
||||
#define DECLARE_LIGHTSHADOWDATA_ACCESS(uniformName) LightShadowData Get##uniformName##Data() { LightShadowData tmp; tmp.ShadowMapSize = uniformName.ShadowMapSize; tmp.Sharpness = uniformName.Sharpness; tmp.Fade = uniformName.Fade; tmp.NormalOffsetScale = uniformName.NormalOffsetScale; tmp.Bias = uniformName.Bias; tmp.FadeDistance = uniformName.FadeDistance; tmp.NumCascades = uniformName.NumCascades; tmp.CascadeSplits = uniformName.CascadeSplits; tmp.ShadowVP[0] = uniformName.ShadowVP[0]; tmp.ShadowVP[1] = uniformName.ShadowVP[1]; tmp.ShadowVP[2] = uniformName.ShadowVP[2]; tmp.ShadowVP[3] = uniformName.ShadowVP[3]; tmp.ShadowVP[4] = uniformName.ShadowVP[4]; tmp.ShadowVP[5] = uniformName.ShadowVP[5]; return tmp; }
|
||||
#else
|
||||
#define DECLARE_LIGHTSHADOWDATA_ACCESS(uniformName) LightShadowData Get##uniformName##Data() { return uniformName; }
|
||||
#endif
|
||||
// Shadow projection tile data for the light
|
||||
struct ShadowTileData
|
||||
{
|
||||
float4 ShadowToAtlas;
|
||||
float4x4 WorldToShadow;
|
||||
};
|
||||
|
||||
// Loads the shadow data of the light in the shadow buffer
|
||||
ShadowData LoadShadowsBuffer(Buffer<float4> shadowsBuffer, uint shadowsBufferAddress)
|
||||
{
|
||||
// This must match C++
|
||||
float4 vector0 = shadowsBuffer.Load(shadowsBufferAddress + 0);
|
||||
float4 vector1 = shadowsBuffer.Load(shadowsBufferAddress + 1);
|
||||
ShadowData shadow;
|
||||
uint packed0x = asuint(vector0.x);
|
||||
shadow.Sharpness = (packed0x & 0x000000ff) * (10.0f / 255.0f);
|
||||
shadow.Fade = ((packed0x & 0x0000ff00) >> 8) * (1.0f / 255.0f);
|
||||
shadow.TilesCount = ((packed0x & 0x00ff0000) >> 16);
|
||||
shadow.FadeDistance = vector0.y;
|
||||
shadow.NormalOffsetScale = vector0.z;
|
||||
shadow.Bias = vector0.w;
|
||||
shadow.CascadeSplits = vector1;
|
||||
return shadow;
|
||||
}
|
||||
|
||||
// Loads the shadow tile data of the light in the shadow buffer
|
||||
ShadowTileData LoadShadowsBufferTile(Buffer<float4> shadowsBuffer, uint shadowsBufferAddress, uint tileIndex)
|
||||
{
|
||||
// This must match C++
|
||||
shadowsBufferAddress += tileIndex * 5 + 2;
|
||||
ShadowTileData tile;
|
||||
tile.ShadowToAtlas = shadowsBuffer.Load(shadowsBufferAddress + 0);
|
||||
tile.WorldToShadow[0] = shadowsBuffer.Load(shadowsBufferAddress + 1);
|
||||
tile.WorldToShadow[1] = shadowsBuffer.Load(shadowsBufferAddress + 2);
|
||||
tile.WorldToShadow[2] = shadowsBuffer.Load(shadowsBufferAddress + 3);
|
||||
tile.WorldToShadow[3] = shadowsBuffer.Load(shadowsBufferAddress + 4);
|
||||
return tile;
|
||||
}
|
||||
|
||||
float3 GetShadowPositionOffset(float offsetScale, float NoL, float3 normal)
|
||||
{
|
||||
@@ -48,8 +73,16 @@ float3 GetShadowPositionOffset(float offsetScale, float NoL, float3 normal)
|
||||
float CalculateSubsurfaceOcclusion(float opacity, float sceneDepth, float shadowMapDepth)
|
||||
{
|
||||
float thickness = max(sceneDepth - shadowMapDepth, 0);
|
||||
float occlusion = 1 - thickness * lerp(1.0f, 100.0f, opacity);
|
||||
float occlusion = 1 - saturate(thickness * lerp(1.0f, 100.0f, opacity));
|
||||
return shadowMapDepth > 0.99f ? 1 : occlusion;
|
||||
}
|
||||
|
||||
float PostProcessShadow(ShadowData lightShadow, float shadow)
|
||||
{
|
||||
// Apply shadow fade and sharpness
|
||||
shadow = saturate((shadow - 0.5) * lightShadow.Sharpness + 0.5);
|
||||
shadow = lerp(1.0f, shadow, lightShadow.Fade);
|
||||
return shadow;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,46 +7,24 @@
|
||||
#include "./Flax/GBufferCommon.hlsl"
|
||||
#include "./Flax/LightingCommon.hlsl"
|
||||
|
||||
// Select shadows filter based on quality
|
||||
// Supported sampling kernel sizes fo each shadowing technique:
|
||||
// CSM: 2, 3, 5, 7, 9
|
||||
// Cube: 2, 5, 12, 29
|
||||
// Spot: 2, 5, 12, 29
|
||||
#if SHADOWS_QUALITY == 0
|
||||
|
||||
#define FilterSizeCSM 2
|
||||
#define FilterSizeCube 2
|
||||
#define FilterSizeSpot 2
|
||||
|
||||
#elif SHADOWS_QUALITY == 1
|
||||
|
||||
#define FilterSizeCSM 3
|
||||
#define FilterSizeCube 5
|
||||
#define FilterSizeSpot 5
|
||||
|
||||
#elif SHADOWS_QUALITY == 2
|
||||
|
||||
#define FilterSizeCSM 5
|
||||
#define FilterSizeCube 12
|
||||
#define FilterSizeSpot 12
|
||||
|
||||
#else // SHADOWS_QUALITY == 3
|
||||
|
||||
#define FilterSizeCSM 7
|
||||
#define FilterSizeCube 12
|
||||
#define FilterSizeSpot 12
|
||||
|
||||
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
|
||||
#define SAMPLE_SHADOW_MAP(shadowMap, shadowUV, sceneDepth) shadowMap.SampleCmpLevelZero(ShadowSamplerLinear, shadowUV, sceneDepth)
|
||||
#define SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowUV, texelOffset, sceneDepth) shadowMap.SampleCmpLevelZero(ShadowSamplerLinear, shadowUV, sceneDepth, texelOffset)
|
||||
#else
|
||||
#define SAMPLE_SHADOW_MAP(shadowMap, shadowUV, sceneDepth) (sceneDepth < shadowMap.SampleLevel(SamplerLinearClamp, shadowUV, 0).r)
|
||||
#define SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowUV, texelOffset, sceneDepth) (sceneDepth < shadowMap.SampleLevel(SamplerLinearClamp, shadowUV, 0, texelOffset).r)
|
||||
#endif
|
||||
|
||||
#if SHADOWS_QUALITY != 0
|
||||
#include "./Flax/PCFKernels.hlsl"
|
||||
#endif
|
||||
float4 GetShadowMask(ShadowSample shadow)
|
||||
{
|
||||
return float4(shadow.SurfaceShadow, shadow.TransmissionShadow, 1, 1);
|
||||
}
|
||||
|
||||
// Gets the cube texture face index to use for shadow map sampling for the given view-to-light direction vector
|
||||
// Where: direction = normalize(worldPosition - lightPosition)
|
||||
int GetCubeFaceIndex(float3 direction)
|
||||
uint GetCubeFaceIndex(float3 direction)
|
||||
{
|
||||
int cubeFaceIndex;
|
||||
uint cubeFaceIndex;
|
||||
float3 absDirection = abs(direction);
|
||||
float maxDirection = max(absDirection.x, max(absDirection.y, absDirection.z));
|
||||
if (maxDirection == absDirection.x)
|
||||
@@ -58,666 +36,230 @@ int GetCubeFaceIndex(float3 direction)
|
||||
return cubeFaceIndex;
|
||||
}
|
||||
|
||||
// Samples the shadow map with a fixed-size PCF kernel optimized with GatherCmpRed.
|
||||
// Uses code from "Fast Conventional Shadow Filtering" by Holger Gruen, in GPU Pro.
|
||||
float SampleShadowMapFixedSizePCF(Texture2DArray shadowMap, float2 shadowMapSize, float sceneDepth, float2 shadowPos, uint cascadeIndex)
|
||||
float2 GetLightShadowAtlasUV(ShadowData shadow, ShadowTileData shadowTile, float3 samplePosition, out float4 shadowPosition)
|
||||
{
|
||||
#if FilterSizeCSM == 2
|
||||
|
||||
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
|
||||
|
||||
return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(shadowPos.xy, cascadeIndex), sceneDepth);
|
||||
|
||||
#else
|
||||
|
||||
return sceneDepth < shadowMap.SampleLevel(SamplerLinearClamp, float3(shadowPos.xy, cascadeIndex), 0).r;
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
const int FS_2 = FilterSizeCSM / 2;
|
||||
float2 tc = shadowPos.xy;
|
||||
float4 s = 0.0f;
|
||||
float2 stc = (shadowMapSize * tc.xy) + float2(0.5f, 0.5f);
|
||||
float2 tcs = floor(stc);
|
||||
float2 fc;
|
||||
int row;
|
||||
int col;
|
||||
float4 v1[FS_2 + 1];
|
||||
float2 v0[FS_2 + 1];
|
||||
float3 baseUV = float3(tc.xy, cascadeIndex);
|
||||
float2 shadowMapSizeInv = 1.0f / shadowMapSize;
|
||||
|
||||
fc.xy = stc - tcs;
|
||||
tc.xy = tcs * shadowMapSizeInv;
|
||||
|
||||
// Loop over the rows
|
||||
UNROLL
|
||||
for (row = -FS_2; row <= FS_2; row += 2)
|
||||
{
|
||||
UNROLL
|
||||
for (col = -FS_2; col <= FS_2; col += 2)
|
||||
{
|
||||
float value = CSMFilterWeights[row + FS_2][col + FS_2];
|
||||
|
||||
if (col > -FS_2)
|
||||
value += CSMFilterWeights[row + FS_2][col + FS_2 - 1];
|
||||
|
||||
if (col < FS_2)
|
||||
value += CSMFilterWeights[row + FS_2][col + FS_2 + 1];
|
||||
|
||||
if (row > -FS_2) {
|
||||
value += CSMFilterWeights[row + FS_2 - 1][col + FS_2];
|
||||
|
||||
if (col < FS_2)
|
||||
value += CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1];
|
||||
|
||||
if (col > -FS_2)
|
||||
value += CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 1];
|
||||
}
|
||||
|
||||
if (value != 0.0f)
|
||||
{
|
||||
// Gather returns xyzw which is counter clockwise order starting with the sample to the lower left of the queried location
|
||||
#if CAN_USE_GATHER
|
||||
|
||||
v1[(col + FS_2) / 2] = shadowMap.GatherCmp(ShadowSampler, baseUV, sceneDepth, int2(col, row));
|
||||
|
||||
#else
|
||||
|
||||
float4 gather;
|
||||
|
||||
gather.x = sceneDepth < shadowMap.SampleLevel(SamplerPointClamp, float3(tc.xy + float2(0, 1) * shadowMapSizeInv, cascadeIndex), 0, int2(col, row)).r;
|
||||
gather.y = sceneDepth < shadowMap.SampleLevel(SamplerPointClamp, float3(tc.xy + float2(1, 1) * shadowMapSizeInv, cascadeIndex), 0, int2(col, row)).r;
|
||||
gather.z = sceneDepth < shadowMap.SampleLevel(SamplerPointClamp, float3(tc.xy + float2(1, 0) * shadowMapSizeInv, cascadeIndex), 0, int2(col, row)).r;
|
||||
gather.w = sceneDepth < shadowMap.SampleLevel(SamplerPointClamp, float3(tc.xy + float2(0, 0) * shadowMapSizeInv, cascadeIndex), 0, int2(col, row)).r;
|
||||
|
||||
v1[(col + FS_2) / 2] = gather;
|
||||
|
||||
#endif
|
||||
}
|
||||
else
|
||||
v1[(col + FS_2) / 2] = 0.0f;
|
||||
|
||||
if (col == -FS_2)
|
||||
{
|
||||
s.x += (1.0f - fc.y) * (v1[0].w * (CSMFilterWeights[row + FS_2][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2] * fc.x)
|
||||
+ v1[0].z * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2 + 1])
|
||||
+ CSMFilterWeights[row + FS_2][col + FS_2 + 1]));
|
||||
s.y += fc.y * (v1[0].x * (CSMFilterWeights[row + FS_2][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2] * fc.x)
|
||||
+ v1[0].y * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2 + 1])
|
||||
+ CSMFilterWeights[row + FS_2][col + FS_2 + 1]));
|
||||
if(row > -FS_2)
|
||||
{
|
||||
s.z += (1.0f - fc.y) * (v0[0].x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2] * fc.x)
|
||||
+ v0[0].y * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1])
|
||||
+ CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1]));
|
||||
s.w += fc.y * (v1[0].w * (CSMFilterWeights[row + FS_2 - 1][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2] * fc.x)
|
||||
+ v1[0].z * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1])
|
||||
+ CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1]));
|
||||
}
|
||||
}
|
||||
else if (col == FS_2)
|
||||
{
|
||||
s.x += (1 - fc.y) * (v1[FS_2].w * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2]) + CSMFilterWeights[row + FS_2][col + FS_2])
|
||||
+ v1[FS_2].z * fc.x * CSMFilterWeights[row + FS_2][col + FS_2]);
|
||||
s.y += fc.y * (v1[FS_2].x * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2] ) + CSMFilterWeights[row + FS_2][col + FS_2])
|
||||
+ v1[FS_2].y * fc.x * CSMFilterWeights[row + FS_2][col + FS_2]);
|
||||
if(row > -FS_2) {
|
||||
s.z += (1 - fc.y) * (v0[FS_2].x * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2])
|
||||
+ CSMFilterWeights[row + FS_2 - 1][col + FS_2])
|
||||
+ v0[FS_2].y * fc.x * CSMFilterWeights[row + FS_2 - 1][col + FS_2]);
|
||||
s.w += fc.y * (v1[FS_2].w * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2])
|
||||
+ CSMFilterWeights[row + FS_2 - 1][col + FS_2])
|
||||
+ v1[FS_2].z * fc.x * CSMFilterWeights[row + FS_2 - 1][col + FS_2]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s.x += (1 - fc.y) * (v1[(col + FS_2) / 2].w * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2 + 0] ) + CSMFilterWeights[row + FS_2][col + FS_2 + 0])
|
||||
+ v1[(col + FS_2) / 2].z * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2 - 0]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2 + 1]) + CSMFilterWeights[row + FS_2][col + FS_2 + 1]));
|
||||
s.y += fc.y * (v1[(col + FS_2) / 2].x * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2-1]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2 + 0]) + CSMFilterWeights[row + FS_2][col + FS_2 + 0])
|
||||
+ v1[(col + FS_2) / 2].y * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2 - 0]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2 + 1]) + CSMFilterWeights[row + FS_2][col + FS_2 + 1]));
|
||||
if(row > -FS_2) {
|
||||
s.z += (1 - fc.y) * (v0[(col + FS_2) / 2].x * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 0]) + CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 0])
|
||||
+ v0[(col + FS_2) / 2].y * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 0]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1]) + CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1]));
|
||||
s.w += fc.y * (v1[(col + FS_2) / 2].w * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 0]) + CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 0])
|
||||
+ v1[(col + FS_2) / 2].z * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 0]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1]) + CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
if (row != FS_2)
|
||||
v0[(col + FS_2) / 2] = v1[(col + FS_2) / 2].xy;
|
||||
}
|
||||
}
|
||||
|
||||
return dot(s, 1.0f) / CSMFilterWeightsSum;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// Helper function for SampleShadowMapOptimizedPCF
|
||||
float SampleShadowMap(Texture2DArray shadowMap, float2 baseUv, float u, float v, float2 shadowMapSizeInv, uint cascadeIndex, float depth)
|
||||
{
|
||||
float2 uv = baseUv + float2(u, v) * shadowMapSizeInv;
|
||||
return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(uv, cascadeIndex), depth);
|
||||
}
|
||||
|
||||
// The method used in The Witness
|
||||
float SampleShadowMapOptimizedPCF(Texture2DArray shadowMap, float2 shadowMapSize, float sceneDepth, float2 shadowPos, uint cascadeIndex)
|
||||
{
|
||||
float2 uv = shadowPos.xy * shadowMapSize; // 1 unit - 1 texel
|
||||
float2 shadowMapSizeInv = 1.0f / shadowMapSize;
|
||||
|
||||
float2 baseUv;
|
||||
baseUv.x = floor(uv.x + 0.5);
|
||||
baseUv.y = floor(uv.y + 0.5);
|
||||
float s = (uv.x + 0.5 - baseUv.x);
|
||||
float t = (uv.y + 0.5 - baseUv.y);
|
||||
baseUv -= float2(0.5, 0.5);
|
||||
baseUv *= shadowMapSizeInv;
|
||||
|
||||
float sum = 0;
|
||||
|
||||
#if FilterSizeCSM == 2
|
||||
|
||||
return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(shadowPos.xy, cascadeIndex), sceneDepth);
|
||||
|
||||
#elif FilterSizeCSM == 3
|
||||
|
||||
float uw0 = (3 - 2 * s);
|
||||
float uw1 = (1 + 2 * s);
|
||||
|
||||
float u0 = (2 - s) / uw0 - 1;
|
||||
float u1 = s / uw1 + 1;
|
||||
|
||||
float vw0 = (3 - 2 * t);
|
||||
float vw1 = (1 + 2 * t);
|
||||
|
||||
float v0 = (2 - t) / vw0 - 1;
|
||||
float v1 = t / vw1 + 1;
|
||||
|
||||
sum += uw0 * vw0 * SampleShadowMap(shadowMap, baseUv, u0, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw0 * SampleShadowMap(shadowMap, baseUv, u1, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw0 * vw1 * SampleShadowMap(shadowMap, baseUv, u0, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw1 * SampleShadowMap(shadowMap, baseUv, u1, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
return sum * 1.0f / 16;
|
||||
|
||||
#elif FilterSizeCSM == 5
|
||||
|
||||
float uw0 = (4 - 3 * s);
|
||||
float uw1 = 7;
|
||||
float uw2 = (1 + 3 * s);
|
||||
|
||||
float u0 = (3 - 2 * s) / uw0 - 2;
|
||||
float u1 = (3 + s) / uw1;
|
||||
float u2 = s / uw2 + 2;
|
||||
|
||||
float vw0 = (4 - 3 * t);
|
||||
float vw1 = 7;
|
||||
float vw2 = (1 + 3 * t);
|
||||
|
||||
float v0 = (3 - 2 * t) / vw0 - 2;
|
||||
float v1 = (3 + t) / vw1;
|
||||
float v2 = t / vw2 + 2;
|
||||
|
||||
sum += uw0 * vw0 * SampleShadowMap(shadowMap, baseUv, u0, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw0 * SampleShadowMap(shadowMap, baseUv, u1, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw0 * SampleShadowMap(shadowMap, baseUv, u2, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
sum += uw0 * vw1 * SampleShadowMap(shadowMap, baseUv, u0, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw1 * SampleShadowMap(shadowMap, baseUv, u1, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw1 * SampleShadowMap(shadowMap, baseUv, u2, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
sum += uw0 * vw2 * SampleShadowMap(shadowMap, baseUv, u0, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw2 * SampleShadowMap(shadowMap, baseUv, u1, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw2 * SampleShadowMap(shadowMap, baseUv, u2, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
return sum * 1.0f / 144;
|
||||
|
||||
#else // FilterSizeCSM == 7
|
||||
|
||||
float uw0 = (5 * s - 6);
|
||||
float uw1 = (11 * s - 28);
|
||||
float uw2 = -(11 * s + 17);
|
||||
float uw3 = -(5 * s + 1);
|
||||
|
||||
float u0 = (4 * s - 5) / uw0 - 3;
|
||||
float u1 = (4 * s - 16) / uw1 - 1;
|
||||
float u2 = -(7 * s + 5) / uw2 + 1;
|
||||
float u3 = -s / uw3 + 3;
|
||||
|
||||
float vw0 = (5 * t - 6);
|
||||
float vw1 = (11 * t - 28);
|
||||
float vw2 = -(11 * t + 17);
|
||||
float vw3 = -(5 * t + 1);
|
||||
|
||||
float v0 = (4 * t - 5) / vw0 - 3;
|
||||
float v1 = (4 * t - 16) / vw1 - 1;
|
||||
float v2 = -(7 * t + 5) / vw2 + 1;
|
||||
float v3 = -t / vw3 + 3;
|
||||
|
||||
sum += uw0 * vw0 * SampleShadowMap(shadowMap, baseUv, u0, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw0 * SampleShadowMap(shadowMap, baseUv, u1, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw0 * SampleShadowMap(shadowMap, baseUv, u2, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw3 * vw0 * SampleShadowMap(shadowMap, baseUv, u3, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
sum += uw0 * vw1 * SampleShadowMap(shadowMap, baseUv, u0, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw1 * SampleShadowMap(shadowMap, baseUv, u1, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw1 * SampleShadowMap(shadowMap, baseUv, u2, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw3 * vw1 * SampleShadowMap(shadowMap, baseUv, u3, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
sum += uw0 * vw2 * SampleShadowMap(shadowMap, baseUv, u0, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw2 * SampleShadowMap(shadowMap, baseUv, u1, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw2 * SampleShadowMap(shadowMap, baseUv, u2, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw3 * vw2 * SampleShadowMap(shadowMap, baseUv, u3, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
sum += uw0 * vw3 * SampleShadowMap(shadowMap, baseUv, u0, v3, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw3 * SampleShadowMap(shadowMap, baseUv, u1, v3, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw3 * SampleShadowMap(shadowMap, baseUv, u2, v3, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw3 * vw3 * SampleShadowMap(shadowMap, baseUv, u3, v3, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
return sum * (1.0f / 2704);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// Samples the shadow from the shadow map cascade
|
||||
float SampleShadowCascade(Texture2DArray shadowMap, float2 shadowMapSize, float sceneDepth, float2 shadowPosition, uint cascadeIndex)
|
||||
{
|
||||
float shadow = SampleShadowMapFixedSizePCF(shadowMap, shadowMapSize, sceneDepth, shadowPosition, cascadeIndex);
|
||||
//float shadow = SampleShadowMapOptimizedPCF(shadowMap, shadowMapSize, sceneDepth, shadowPosition, cascadeIndex);
|
||||
return shadow;
|
||||
}
|
||||
|
||||
// Samples the shadow for the given directional light (cascaded shadow map sampling)
|
||||
float SampleShadow(LightData light, LightShadowData shadow, Texture2DArray shadowMap, float3 worldPosition, float viewDepth)
|
||||
{
|
||||
// Create a blend factor which is one before and at the fade plane
|
||||
float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.NumCascades - 1] + shadow.FadeDistance) / shadow.FadeDistance);
|
||||
BRANCH
|
||||
if (fade >= 1.0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Figure out which cascade to sample from
|
||||
uint cascadeIndex = 0;
|
||||
for (uint i = 0; i < shadow.NumCascades - 1; i++)
|
||||
{
|
||||
if (viewDepth > shadow.CascadeSplits[i])
|
||||
cascadeIndex = i + 1;
|
||||
}
|
||||
|
||||
// Project into shadow space
|
||||
float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cascadeIndex]);
|
||||
shadowPosition.xy /= shadowPosition.w;
|
||||
// Project into shadow space (WorldToShadow is pre-multiplied to convert Clip Space to UV Space)
|
||||
shadowPosition = mul(float4(samplePosition, 1.0f), shadowTile.WorldToShadow);
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
shadowPosition.xyz /= shadowPosition.w;
|
||||
|
||||
// Sample shadow
|
||||
float result = SampleShadowCascade(shadowMap, shadow.ShadowMapSize, shadowPosition.z, shadowPosition.xy, cascadeIndex);
|
||||
// UV Space -> Atlas Tile UV Space
|
||||
float2 shadowMapUV = saturate(shadowPosition.xy);
|
||||
shadowMapUV = shadowMapUV * shadowTile.ShadowToAtlas.xy + shadowTile.ShadowToAtlas.zw;
|
||||
return shadowMapUV;
|
||||
}
|
||||
|
||||
// Increase the sharpness for higher cascades to match the filter radius
|
||||
const float SharpnessScale[MaxNumCascades] = { 1.0f, 1.5f, 3.0f, 3.5f };
|
||||
float sharpness = shadow.Sharpness * SharpnessScale[cascadeIndex];
|
||||
|
||||
#if CSM_BLENDING
|
||||
// Sample the next cascade, and blend between the two results to smooth the transition
|
||||
const float BlendThreshold = 0.1f;
|
||||
float nextSplit = shadow.CascadeSplits[cascadeIndex];
|
||||
float splitSize = cascadeIndex == 0 ? nextSplit : nextSplit - shadow.CascadeSplits[cascadeIndex - 1];
|
||||
float splitDist = (nextSplit - viewDepth) / splitSize;
|
||||
BRANCH
|
||||
if (splitDist <= BlendThreshold && cascadeIndex != shadow.NumCascades - 1)
|
||||
{
|
||||
// Find the position of this pixel in light space of next cascade
|
||||
shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cascadeIndex + 1]);
|
||||
shadowPosition.xy /= shadowPosition.w;
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
|
||||
// Sample next cascade and blur result
|
||||
float nextSplitShadow = SampleShadowCascade(shadowMap, shadow.ShadowMapSize, shadowPosition.z, shadowPosition.xy, cascadeIndex + 1);
|
||||
float lerpAmount = smoothstep(0.0f, BlendThreshold, splitDist);
|
||||
lerpAmount = splitDist / BlendThreshold;
|
||||
result = lerp(nextSplitShadow, result, lerpAmount);
|
||||
|
||||
// Blur sharpness as well
|
||||
sharpness = lerp(shadow.Sharpness * SharpnessScale[cascadeIndex + 1], sharpness, lerpAmount);
|
||||
}
|
||||
float SampleShadowMap(Texture2D<float> shadowMap, float2 shadowMapUV, float sceneDepth)
|
||||
{
|
||||
// Single hardware sample with filtering
|
||||
float result = SAMPLE_SHADOW_MAP(shadowMap, shadowMapUV, sceneDepth);
|
||||
|
||||
#if SHADOWS_QUALITY == 1
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(-1, 0), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(0, -1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(0, 1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(1, 0), sceneDepth);
|
||||
result = result * (1.0f / 4.0);
|
||||
#elif SHADOWS_QUALITY == 2 || SHADOWS_QUALITY == 3
|
||||
// TODO: implement Percentage-Closer Soft Shadows (PCSS) for Ultra quality
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(-1, -1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(-1, 0), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(-1, 1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(0, -1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(0, 1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(1, -1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(1, 0), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(1, 1), sceneDepth);
|
||||
result = result * (1.0f / 9.0);
|
||||
#endif
|
||||
|
||||
// Apply shadow fade and sharpness
|
||||
result = saturate((result - 0.5) * sharpness + 0.5);
|
||||
result = lerp(1.0f, result, (1 - fade) * shadow.Fade);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Samples the shadow for the given directional light (cascaded shadow map sampling) for the material surface (supports subsurface shadowing)
|
||||
float SampleShadow(LightData light, LightShadowData shadow, Texture2DArray shadowMap, GBufferSample gBuffer, out float subsurfaceShadow)
|
||||
// Samples the shadow for the given directional light on the material surface (supports subsurface shadowing)
|
||||
ShadowSample SampleDirectionalLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, GBufferSample gBuffer)
|
||||
{
|
||||
subsurfaceShadow = 1;
|
||||
|
||||
// Create a blend factor which is one before and at the fade plane
|
||||
float viewDepth = gBuffer.ViewPos.z;
|
||||
float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.NumCascades - 1] + shadow.FadeDistance) / shadow.FadeDistance);
|
||||
BRANCH
|
||||
if (fade >= 1.0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Figure out which cascade to sample from
|
||||
uint cascadeIndex = 0;
|
||||
for (uint i = 0; i < shadow.NumCascades - 1; i++)
|
||||
{
|
||||
if (viewDepth > shadow.CascadeSplits[i])
|
||||
cascadeIndex = i + 1;
|
||||
}
|
||||
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
// Subsurface shadowing
|
||||
BRANCH
|
||||
if (IsSubsurfaceMode(gBuffer.ShadingModel))
|
||||
{
|
||||
// Get subsurface material info
|
||||
float opacity = gBuffer.CustomData.a;
|
||||
|
||||
// Project into shadow space
|
||||
float4 shadowPosition = mul(float4(gBuffer.WorldPos, 1.0f), shadow.ShadowVP[cascadeIndex]);
|
||||
shadowPosition.xy /= shadowPosition.w;
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
|
||||
// Sample shadow map (single hardware sample with hardware filtering)
|
||||
float shadowMapDepth = shadowMap.SampleLevel(SamplerLinearClamp, float3(shadowPosition.xy, cascadeIndex), 0).r;
|
||||
subsurfaceShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
|
||||
|
||||
// Apply shadow fade
|
||||
subsurfaceShadow = lerp(1.0f, subsurfaceShadow, (1 - fade) * shadow.Fade);
|
||||
}
|
||||
#endif
|
||||
|
||||
float3 samplePosWS = gBuffer.WorldPos;
|
||||
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Skip if surface is in a full shadow
|
||||
float NoL = dot(gBuffer.Normal, light.Direction);
|
||||
BRANCH
|
||||
if (NoL <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply normal offset bias
|
||||
samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
|
||||
if (NoL <= 0
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
&& !IsSubsurfaceMode(gBuffer.ShadingModel)
|
||||
#endif
|
||||
)
|
||||
return (ShadowSample)0;
|
||||
#endif
|
||||
|
||||
// Sample shadow
|
||||
return SampleShadow(light, shadow, shadowMap, samplePosWS, viewDepth);
|
||||
}
|
||||
ShadowSample result;
|
||||
result.SurfaceShadow = 1;
|
||||
result.TransmissionShadow = 1;
|
||||
|
||||
// Load shadow data
|
||||
if (light.ShadowsBufferAddress == 0)
|
||||
return result; // No shadow assigned
|
||||
ShadowData shadow = LoadShadowsBuffer(shadowsBuffer, light.ShadowsBufferAddress);
|
||||
|
||||
// Samples the shadow for the given spot light (PCF shadow map sampling)
|
||||
float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap, float3 worldPosition)
|
||||
{
|
||||
float3 toLight = light.Position - worldPosition;
|
||||
float toLightLength = length(toLight);
|
||||
float3 L = toLight / toLightLength;
|
||||
#if LIGHTING_NO_DIRECTIONAL
|
||||
float dirCheck = 1.0f;
|
||||
#else
|
||||
float dirCheck = dot(-light.Direction, L);
|
||||
#endif
|
||||
|
||||
// Skip pixels outside of the light influence
|
||||
// Create a blend factor which is one before and at the fade plane
|
||||
float viewDepth = gBuffer.ViewPos.z;
|
||||
float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.TilesCount - 1] + shadow.FadeDistance) / shadow.FadeDistance);
|
||||
BRANCH
|
||||
if (toLightLength > light.Radius || dirCheck < 0)
|
||||
if (fade >= 1.0)
|
||||
return result;
|
||||
|
||||
// Figure out which cascade to sample from
|
||||
uint cascadeIndex = 0;
|
||||
for (uint i = 0; i < shadow.TilesCount - 1; i++)
|
||||
{
|
||||
return 1;
|
||||
if (viewDepth > shadow.CascadeSplits[i])
|
||||
cascadeIndex = i + 1;
|
||||
}
|
||||
ShadowTileData shadowTile = LoadShadowsBufferTile(shadowsBuffer, light.ShadowsBufferAddress, cascadeIndex);
|
||||
|
||||
// Negate direction and use normalized value
|
||||
toLight = -L;
|
||||
|
||||
// Project into shadow space
|
||||
float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[0]);
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
shadowPosition.xyz /= shadowPosition.w;
|
||||
|
||||
float2 shadowMapUVs = shadowPosition.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f);
|
||||
|
||||
#if FilterSizeSpot == 2
|
||||
|
||||
// Use single hardware sample with filtering
|
||||
float result = shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, shadowMapUVs, shadowPosition.z);
|
||||
|
||||
#else
|
||||
|
||||
float3 sideVector = normalize(cross(toLight, float3(0, 0, 1)));
|
||||
float3 upVector = cross(sideVector, toLight);
|
||||
|
||||
float shadowMapSizeInv = 1.0f / shadow.ShadowMapSize.x;
|
||||
sideVector *= shadowMapSizeInv;
|
||||
upVector *= shadowMapSizeInv;
|
||||
|
||||
// Use PCF filter
|
||||
float result = 0;
|
||||
UNROLL
|
||||
for(int i = 0; i < FilterSizeCube; i++)
|
||||
{
|
||||
float2 samplePos = shadowMapUVs + sideVector.xy * PCFDiscSamples[i].x + upVector.xy * PCFDiscSamples[i].y;
|
||||
result += shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, samplePos, shadowPosition.z);
|
||||
}
|
||||
result *= (1.0f / FilterSizeCube);
|
||||
|
||||
float3 samplePosition = gBuffer.WorldPos;
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Apply normal offset bias
|
||||
samplePosition += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
|
||||
#endif
|
||||
|
||||
// Apply shadow fade and sharpness
|
||||
result = saturate((result - 0.5) * shadow.Sharpness + 0.5);
|
||||
result = lerp(1.0f, result, shadow.Fade);
|
||||
// Project position into shadow atlas UV
|
||||
float4 shadowPosition;
|
||||
float2 shadowMapUV = GetLightShadowAtlasUV(shadow, shadowTile, samplePosition, shadowPosition);
|
||||
|
||||
// Sample shadow map
|
||||
result.SurfaceShadow = SampleShadowMap(shadowMap, shadowMapUV, shadowPosition.z);
|
||||
|
||||
// Increase the sharpness for higher cascades to match the filter radius
|
||||
const float SharpnessScale[MaxNumCascades] = { 1.0f, 1.5f, 3.0f, 3.5f };
|
||||
shadow.Sharpness *= SharpnessScale[cascadeIndex];
|
||||
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
// Subsurface shadowing
|
||||
BRANCH
|
||||
if (IsSubsurfaceMode(gBuffer.ShadingModel))
|
||||
{
|
||||
float opacity = gBuffer.CustomData.a;
|
||||
shadowMapUV = GetLightShadowAtlasUV(shadow, shadowTile, gBuffer.WorldPos, shadowPosition);
|
||||
float shadowMapDepth = shadowMap.SampleLevel(SamplerLinearClamp, shadowMapUV, 0).r;
|
||||
result.TransmissionShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
|
||||
result.TransmissionShadow = PostProcessShadow(shadow, result.TransmissionShadow);
|
||||
}
|
||||
#endif
|
||||
|
||||
result.SurfaceShadow = PostProcessShadow(shadow, result.SurfaceShadow);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Samples the shadow for the given spot light (PCF shadow map sampling) for the material surface (supports subsurface shadowing)
|
||||
float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap, GBufferSample gBuffer, out float subsurfaceShadow)
|
||||
// Samples the shadow for the given local light on the material surface (supports subsurface shadowing)
|
||||
ShadowSample SampleLocalLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, GBufferSample gBuffer, float3 L, float toLightLength, uint tileIndex)
|
||||
{
|
||||
subsurfaceShadow = 1;
|
||||
float3 toLight = light.Position - gBuffer.WorldPos;
|
||||
float toLightLength = length(toLight);
|
||||
float3 L = toLight / toLightLength;
|
||||
#if LIGHTING_NO_DIRECTIONAL
|
||||
float dirCheck = 1.0f;
|
||||
#else
|
||||
float dirCheck = dot(-light.Direction, L);
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Skip if surface is in a full shadow
|
||||
float NoL = dot(gBuffer.Normal, L);
|
||||
BRANCH
|
||||
if (NoL <= 0
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
&& !IsSubsurfaceMode(gBuffer.ShadingModel)
|
||||
#endif
|
||||
)
|
||||
return (ShadowSample)0;
|
||||
#endif
|
||||
|
||||
ShadowSample result;
|
||||
result.SurfaceShadow = 1;
|
||||
result.TransmissionShadow = 1;
|
||||
|
||||
// Skip pixels outside of the light influence
|
||||
BRANCH
|
||||
if (toLightLength > light.Radius || dirCheck < 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (toLightLength > light.Radius)
|
||||
return result;
|
||||
|
||||
// Load shadow data
|
||||
if (light.ShadowsBufferAddress == 0)
|
||||
return result; // No shadow assigned
|
||||
ShadowData shadow = LoadShadowsBuffer(shadowsBuffer, light.ShadowsBufferAddress);
|
||||
ShadowTileData shadowTile = LoadShadowsBufferTile(shadowsBuffer, light.ShadowsBufferAddress, tileIndex);
|
||||
|
||||
float3 samplePosition = gBuffer.WorldPos;
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Apply normal offset bias
|
||||
samplePosition += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
|
||||
#endif
|
||||
|
||||
// Project position into shadow atlas UV
|
||||
float4 shadowPosition;
|
||||
float2 shadowMapUV = GetLightShadowAtlasUV(shadow, shadowTile, samplePosition, shadowPosition);
|
||||
|
||||
// Sample shadow map
|
||||
result.SurfaceShadow = SampleShadowMap(shadowMap, shadowMapUV, shadowPosition.z);
|
||||
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
// Subsurface shadowing
|
||||
BRANCH
|
||||
if (IsSubsurfaceMode(gBuffer.ShadingModel))
|
||||
{
|
||||
// Get subsurface material info
|
||||
float opacity = gBuffer.CustomData.a;
|
||||
|
||||
// Project into shadow space
|
||||
float4 shadowPosition = mul(float4(gBuffer.WorldPos, 1.0f), shadow.ShadowVP[0]);
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
shadowPosition.xyz /= shadowPosition.w;
|
||||
|
||||
// Sample shadow map (use single hardware sample with filtering)
|
||||
float shadowMapDepth = shadowMap.SampleLevel(SamplerLinearClamp, shadowPosition.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f), 0).r;
|
||||
subsurfaceShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
|
||||
|
||||
// Apply shadow fade
|
||||
subsurfaceShadow = lerp(1.0f, subsurfaceShadow, shadow.Fade);
|
||||
shadowMapUV = GetLightShadowAtlasUV(shadow, shadowTile, gBuffer.WorldPos, shadowPosition);
|
||||
float shadowMapDepth = shadowMap.SampleLevel(SamplerLinearClamp, shadowMapUV, 0).r;
|
||||
result.TransmissionShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
|
||||
result.TransmissionShadow = PostProcessShadow(shadow, result.TransmissionShadow);
|
||||
}
|
||||
#endif
|
||||
|
||||
float3 samplePosWS = gBuffer.WorldPos;
|
||||
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Skip if surface is in a full shadow
|
||||
float NoL = dot(gBuffer.Normal, L);
|
||||
BRANCH
|
||||
if (NoL <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply normal offset bias
|
||||
samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
|
||||
#endif
|
||||
|
||||
// Sample shadow
|
||||
return SampleShadow(light, shadow, shadowMap, samplePosWS);
|
||||
}
|
||||
|
||||
// Samples the shadow for the given point light (PCF shadow map sampling)
|
||||
float SampleShadow(LightData light, LightShadowData shadow, TextureCube<float> shadowMap, float3 worldPosition)
|
||||
{
|
||||
float3 toLight = light.Position - worldPosition;
|
||||
float toLightLength = length(toLight);
|
||||
float3 L = toLight / toLightLength;
|
||||
|
||||
// Skip pixels outside of the light influence
|
||||
BRANCH
|
||||
if (toLightLength > light.Radius)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Negate direction and use normalized value
|
||||
toLight = -L;
|
||||
|
||||
// Figure out which cube face we're sampling from
|
||||
int cubeFaceIndex = GetCubeFaceIndex(toLight);
|
||||
|
||||
// Project into shadow space
|
||||
float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cubeFaceIndex]);
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
shadowPosition.xyz /= shadowPosition.w;
|
||||
|
||||
#if FilterSizeCube == 2
|
||||
|
||||
// Use single hardware sample with filtering
|
||||
float result = shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, toLight, shadowPosition.z);
|
||||
|
||||
#else
|
||||
|
||||
float3 sideVector = normalize(cross(toLight, float3(0, 0, 1)));
|
||||
float3 upVector = cross(sideVector, toLight);
|
||||
|
||||
float shadowMapSizeInv = 1.0f / shadow.ShadowMapSize.x;
|
||||
sideVector *= shadowMapSizeInv;
|
||||
upVector *= shadowMapSizeInv;
|
||||
|
||||
// Use PCF filter
|
||||
float result = 0;
|
||||
UNROLL
|
||||
for (int i = 0; i < FilterSizeCube; i++)
|
||||
{
|
||||
float3 cubeSamplePos = toLight + sideVector * PCFDiscSamples[i].x + upVector * PCFDiscSamples[i].y;
|
||||
result += shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, cubeSamplePos, shadowPosition.z);
|
||||
}
|
||||
result *= (1.0f / FilterSizeCube);
|
||||
|
||||
#endif
|
||||
|
||||
// Apply shadow fade and sharpness
|
||||
result = saturate((result - 0.5) * shadow.Sharpness + 0.5);
|
||||
result = lerp(1.0f, result, shadow.Fade);
|
||||
|
||||
result.SurfaceShadow = PostProcessShadow(shadow, result.SurfaceShadow);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Samples the shadow for the given point light (PCF shadow map sampling) for the material surface (supports subsurface shadowing)
|
||||
float SampleShadow(LightData light, LightShadowData shadow, TextureCube<float> shadowMap, GBufferSample gBuffer, out float subsurfaceShadow)
|
||||
// Samples the shadow for the given spot light on the material surface (supports subsurface shadowing)
|
||||
ShadowSample SampleSpotLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, GBufferSample gBuffer)
|
||||
{
|
||||
float3 toLight = light.Position - gBuffer.WorldPos;
|
||||
float toLightLength = length(toLight);
|
||||
float3 L = toLight / toLightLength;
|
||||
return SampleLocalLightShadow(light, shadowsBuffer, shadowMap, gBuffer, L, toLightLength, 0);
|
||||
}
|
||||
|
||||
// Samples the shadow for the given point light on the material surface (supports subsurface shadowing)
|
||||
ShadowSample SamplePointLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, GBufferSample gBuffer)
|
||||
{
|
||||
subsurfaceShadow = 1;
|
||||
float3 toLight = light.Position - gBuffer.WorldPos;
|
||||
float toLightLength = length(toLight);
|
||||
float3 L = toLight / toLightLength;
|
||||
|
||||
// Skip pixels outside of the light influence
|
||||
BRANCH
|
||||
if (toLightLength > light.Radius)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Negate direction and use normalized value
|
||||
toLight = -L;
|
||||
|
||||
// Figure out which cube face we're sampling from
|
||||
int cubeFaceIndex = GetCubeFaceIndex(toLight);
|
||||
uint cubeFaceIndex = GetCubeFaceIndex(-L);
|
||||
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
// Subsurface shadowing
|
||||
BRANCH
|
||||
if (IsSubsurfaceMode(gBuffer.ShadingModel))
|
||||
{
|
||||
// Get subsurface material info
|
||||
float opacity = gBuffer.CustomData.a;
|
||||
return SampleLocalLightShadow(light, shadowsBuffer, shadowMap, gBuffer, L, toLightLength, cubeFaceIndex);
|
||||
}
|
||||
|
||||
// Project into shadow space
|
||||
float4 shadowPosition = mul(float4(gBuffer.WorldPos, 1.0f), shadow.ShadowVP[cubeFaceIndex]);
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
shadowPosition.xyz /= shadowPosition.w;
|
||||
GBufferSample GetDummyGBufferSample(float3 worldPosition)
|
||||
{
|
||||
GBufferSample gBuffer = (GBufferSample)0;
|
||||
gBuffer.ShadingModel = SHADING_MODEL_LIT;
|
||||
gBuffer.WorldPos = worldPosition;
|
||||
return gBuffer;
|
||||
}
|
||||
|
||||
// Sample shadow map (use single hardware sample with filtering)
|
||||
float shadowMapDepth = shadowMap.SampleLevel(SamplerLinearClamp, toLight, 0).r;
|
||||
subsurfaceShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
|
||||
// Samples the shadow for the given directional light at custom location
|
||||
ShadowSample SampleDirectionalLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, float3 worldPosition, float viewDepth)
|
||||
{
|
||||
GBufferSample gBuffer = GetDummyGBufferSample(worldPosition);
|
||||
gBuffer.ViewPos.z = viewDepth;
|
||||
return SampleDirectionalLightShadow(light, shadowsBuffer, shadowMap, gBuffer);
|
||||
}
|
||||
|
||||
// Apply shadow fade
|
||||
subsurfaceShadow = lerp(1.0f, subsurfaceShadow, shadow.Fade);
|
||||
}
|
||||
#endif
|
||||
|
||||
float3 samplePosWS = gBuffer.WorldPos;
|
||||
// Samples the shadow for the given spot light at custom location
|
||||
ShadowSample SampleSpotLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, float3 worldPosition)
|
||||
{
|
||||
GBufferSample gBuffer = GetDummyGBufferSample(worldPosition);
|
||||
return SampleSpotLightShadow(light, shadowsBuffer, shadowMap, gBuffer);
|
||||
}
|
||||
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Skip if surface is in a full shadow
|
||||
float NoL = dot(gBuffer.Normal, L);
|
||||
BRANCH
|
||||
if (NoL <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply normal offset bias
|
||||
samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
|
||||
#endif
|
||||
|
||||
// Sample shadow
|
||||
return SampleShadow(light, shadow, shadowMap, samplePosWS);
|
||||
// Samples the shadow for the given point light at custom location
|
||||
ShadowSample SamplePointLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, float3 worldPosition)
|
||||
{
|
||||
GBufferSample gBuffer = GetDummyGBufferSample(worldPosition);
|
||||
return SamplePointLightShadow(light, shadowsBuffer, shadowMap, gBuffer);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// "Physically Based and Unified Volumetric Rendering in Frostbite" - Sebastien Hillaire at Siggraph 2015
|
||||
|
||||
#define NO_GBUFFER_SAMPLING
|
||||
#define LIGHTING_NO_DIRECTIONAL 1
|
||||
|
||||
// Debug voxels world space positions
|
||||
#define DEBUG_VOXEL_WS_POS 0
|
||||
@@ -24,11 +25,10 @@ struct SkyLightData
|
||||
float3 MultiplyColor;
|
||||
float VolumetricScatteringIntensity;
|
||||
float3 AdditiveColor;
|
||||
float Dummt0;
|
||||
float Dummy0;
|
||||
};
|
||||
|
||||
META_CB_BEGIN(0, Data)
|
||||
|
||||
GBufferData GBuffer;
|
||||
|
||||
float3 GlobalAlbedo;
|
||||
@@ -54,14 +54,11 @@ float4x4 PrevWorldToClip;
|
||||
float4 FrameJitterOffsets[8];
|
||||
|
||||
LightData DirectionalLight;
|
||||
LightShadowData DirectionalLightShadow;
|
||||
SkyLightData SkyLight;
|
||||
DDGIData DDGI;
|
||||
|
||||
META_CB_END
|
||||
|
||||
META_CB_BEGIN(1, PerLight)
|
||||
|
||||
float2 SliceToDepth;
|
||||
int MinZ;
|
||||
float LocalLightScatteringIntensity;
|
||||
@@ -70,8 +67,6 @@ float4 ViewSpaceBoundingSphere;
|
||||
float4x4 ViewToVolumeClip;
|
||||
|
||||
LightData LocalLight;
|
||||
LightShadowData LocalLightShadow;
|
||||
|
||||
META_CB_END
|
||||
|
||||
// The Henyey-Greenstein phase function
|
||||
@@ -162,28 +157,8 @@ void GS_WriteToSlice(triangle Quad_VS2GS input[3], inout TriangleStream<Quad_GS2
|
||||
}
|
||||
|
||||
#if USE_SHADOW
|
||||
|
||||
// Shadow Maps
|
||||
TextureCube<float> ShadowMapCube : register(t5);
|
||||
Texture2D ShadowMapSpot : register(t6);
|
||||
|
||||
float ComputeVolumeShadowing(float3 worldPosition, bool isSpotLight)
|
||||
{
|
||||
float shadow = 1;
|
||||
|
||||
// TODO: use single shadowmaps atlas for whole scene (with slots) - same code path for spot and point lights
|
||||
if (isSpotLight)
|
||||
{
|
||||
shadow = SampleShadow(LocalLight, LocalLightShadow, ShadowMapSpot, worldPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
shadow = SampleShadow(LocalLight, LocalLightShadow, ShadowMapCube, worldPosition);
|
||||
}
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
Texture2D<float> ShadowMap : register(t0);
|
||||
Buffer<float4> ShadowsBuffer : register(t1);
|
||||
#endif
|
||||
|
||||
META_PS(true, FEATURE_LEVEL_SM5)
|
||||
@@ -225,15 +200,22 @@ float4 PS_InjectLight(Quad_GS2PS input) : SV_Target0
|
||||
GetRadialLightAttenuation(LocalLight, isSpotLight, float3(0, 0, 1), distanceSqr, distanceBias * distanceBias, toLight, L, NoL, attenuation);
|
||||
|
||||
// Peek the shadow
|
||||
float shadowFactor = 1.0f;
|
||||
float shadow = 1.0f;
|
||||
#if USE_SHADOW
|
||||
if (attenuation > 0)
|
||||
{
|
||||
shadowFactor = ComputeVolumeShadowing(positionWS, isSpotLight);
|
||||
if (isSpotLight)
|
||||
{
|
||||
shadow = SampleSpotLightShadow(LocalLight, ShadowsBuffer, ShadowMap, positionWS).SurfaceShadow;
|
||||
}
|
||||
else
|
||||
{
|
||||
shadow = SamplePointLightShadow(LocalLight, ShadowsBuffer, ShadowMap, positionWS).SurfaceShadow;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
scattering.rgb += LocalLight.Color * (GetPhase(PhaseG, dot(L, -cameraVector)) * attenuation * shadowFactor * LocalLightScatteringIntensity);
|
||||
scattering.rgb += LocalLight.Color * (GetPhase(PhaseG, dot(L, -cameraVector)) * attenuation * shadow * LocalLightScatteringIntensity);
|
||||
}
|
||||
|
||||
scattering.rgb /= (float)samplesCount;
|
||||
@@ -281,13 +263,14 @@ Texture3D<float4> VBufferA : register(t0);
|
||||
Texture3D<float4> VBufferB : register(t1);
|
||||
Texture3D<float4> LightScatteringHistory : register(t2);
|
||||
Texture3D<float4> LocalShadowedLightScattering : register(t3);
|
||||
Texture2DArray ShadowMapCSM : register(t4);
|
||||
Texture2D<float> ShadowMap : register(t4);
|
||||
Buffer<float4> ShadowsBuffer : register(t5);
|
||||
#if USE_DDGI
|
||||
Texture2D<snorm float4> ProbesData : register(t5);
|
||||
Texture2D<float4> ProbesDistance : register(t6);
|
||||
Texture2D<float4> ProbesIrradiance : register(t7);
|
||||
Texture2D<snorm float4> ProbesData : register(t6);
|
||||
Texture2D<float4> ProbesDistance : register(t7);
|
||||
Texture2D<float4> ProbesIrradiance : register(t8);
|
||||
#else
|
||||
TextureCube SkyLightImage : register(t5);
|
||||
TextureCube SkyLightImage : register(t6);
|
||||
#endif
|
||||
|
||||
META_CS(true, FEATURE_LEVEL_SM5)
|
||||
@@ -319,16 +302,8 @@ void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_
|
||||
float3 cameraVectorNormalized = cameraVector / cameraVectorLength;
|
||||
|
||||
// Directional light
|
||||
BRANCH
|
||||
if (DirectionalLightShadow.NumCascades < 10) // NumCascades==10 if no dir light
|
||||
{
|
||||
// Try to sample CSM shadow at the voxel position
|
||||
float shadow = 1;
|
||||
if (DirectionalLightShadow.NumCascades > 0)
|
||||
{
|
||||
shadow = SampleShadow(DirectionalLight, DirectionalLightShadow, ShadowMapCSM, positionWS, cameraVectorLength);
|
||||
}
|
||||
|
||||
{
|
||||
float shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, positionWS, cameraVectorLength).SurfaceShadow;
|
||||
lightScattering += DirectionalLight.Color * (8 * shadow * GetPhase(PhaseG, dot(DirectionalLight.Direction, cameraVectorNormalized)));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user