Refactor shadows rendering to use Shadow Map Atlas

This commit is contained in:
Wojtek Figat
2024-04-04 12:54:07 +02:00
parent 017def29d4
commit 61323f8526
31 changed files with 1115 additions and 1826 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)));
}