diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h
index 1ac5ac5b8..f389dcdea 100644
--- a/WickedEngine/shaders/ShaderInterop_Renderer.h
+++ b/WickedEngine/shaders/ShaderInterop_Renderer.h
@@ -1461,5 +1461,13 @@ CBUFFER(TrailRendererCB, CBSLOT_TRAILRENDERER)
float g_xTrailCameraFar;
};
+struct ShadowFilterPush
+{
+ uint4 rect;
+ float2 atlas_resolution_rcp;
+ float spread;
+ float padding;
+};
+
#endif // WI_SHADERINTEROP_RENDERER_H
diff --git a/WickedEngine/shaders/Shaders_SOURCE.vcxitems b/WickedEngine/shaders/Shaders_SOURCE.vcxitems
index 14916598f..23c821e8f 100644
--- a/WickedEngine/shaders/Shaders_SOURCE.vcxitems
+++ b/WickedEngine/shaders/Shaders_SOURCE.vcxitems
@@ -951,6 +951,10 @@
+
+ Compute
+ 4.0
+
Compute
diff --git a/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters b/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters
index cd42dde36..1351a0b13 100644
--- a/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters
+++ b/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters
@@ -1172,6 +1172,9 @@
CS
+
+ CS
+
diff --git a/WickedEngine/shaders/aerialPerspectiveCS.hlsl b/WickedEngine/shaders/aerialPerspectiveCS.hlsl
index 2bcd5611a..c62a677c2 100644
--- a/WickedEngine/shaders/aerialPerspectiveCS.hlsl
+++ b/WickedEngine/shaders/aerialPerspectiveCS.hlsl
@@ -1,4 +1,3 @@
-#define DISABLE_SOFT_SHADOWMAP
#define TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK
#include "globals.hlsli"
#include "skyAtmosphere.hlsli"
diff --git a/WickedEngine/shaders/emittedparticlePS_soft_lighting.hlsl b/WickedEngine/shaders/emittedparticlePS_soft_lighting.hlsl
index 4d4cef643..b62d7ecd3 100644
--- a/WickedEngine/shaders/emittedparticlePS_soft_lighting.hlsl
+++ b/WickedEngine/shaders/emittedparticlePS_soft_lighting.hlsl
@@ -1,3 +1,2 @@
#define EMITTEDPARTICLE_LIGHTING
-//#define DISABLE_SOFT_SHADOWMAP
#include "emittedparticlePS_soft.hlsl"
diff --git a/WickedEngine/shaders/emittedparticle_simulateCS.hlsl b/WickedEngine/shaders/emittedparticle_simulateCS.hlsl
index e29b361d1..944e3d43e 100644
--- a/WickedEngine/shaders/emittedparticle_simulateCS.hlsl
+++ b/WickedEngine/shaders/emittedparticle_simulateCS.hlsl
@@ -57,29 +57,28 @@ void main(uint3 DTid : SV_DispatchThreadID, uint Gid : SV_GroupIndex)
// Blocker shadow map check using previous frame:
// This is extended with blocker position calculation for splashing
- [branch]
- if ((xEmitterOptions & EMITTER_OPTION_BIT_USE_RAIN_BLOCKER) && GetFrame().texture_shadowatlas_index >= 0 && any(GetFrame().rain_blocker_mad_prev))
- {
- Texture2D texture_shadowatlas = bindless_textures[GetFrame().texture_shadowatlas_index];
- float3 shadow_pos = mul(GetFrame().rain_blocker_matrix_prev, float4(particle.position, 1)).xyz;
- float3 shadow_uv = clipspace_to_uv(shadow_pos);
- if (is_saturated(shadow_uv))
- {
- shadow_uv.xy = mad(shadow_uv.xy, GetFrame().rain_blocker_mad_prev.xy, GetFrame().rain_blocker_mad_prev.zw);
- float shadow = texture_shadowatlas.SampleLevel(sampler_point_clamp, shadow_uv.xy, 0).r;
+ //[branch]
+ //if ((xEmitterOptions & EMITTER_OPTION_BIT_USE_RAIN_BLOCKER) && any(GetFrame().rain_blocker_mad_prev))
+ //{
+ // float3 shadow_pos = mul(GetFrame().rain_blocker_matrix_prev, float4(particle.position, 1)).xyz;
+ // float3 shadow_uv = clipspace_to_uv(shadow_pos);
+ // if (is_saturated(shadow_uv))
+ // {
+ // shadow_uv.xy = mad(shadow_uv.xy, GetFrame().rain_blocker_mad_prev.xy, GetFrame().rain_blocker_mad_prev.zw);
+ // float shadow = sample_shadow(shadow_uv.xy, shadow_pos.z).r;
- if(shadow > shadow_pos.z)
- {
- // Under blocker, make a splash by placing above blocker and modulating some params:
- float3 blockerPos = reconstruct_position(clipspace_to_uv(shadow_pos.xy), shadow, GetFrame().rain_blocker_matrix_inverse_prev);
- particle.velocity = 0;
- float splashSize = GetWeather().rain_splash_scale;
- particle.position = blockerPos + float3(0, splashSize * 0.5, 0);
- particle.sizeBeginEnd = splashSize;
- particle.life = 0.15;
- }
- }
- }
+ // if(shadow < 1)
+ // {
+ // // Under blocker, make a splash by placing above blocker and modulating some params:
+ // float3 blockerPos = reconstruct_position(clipspace_to_uv(shadow_pos.xy), shadow, GetFrame().rain_blocker_matrix_inverse_prev);
+ // particle.velocity = 0;
+ // float splashSize = GetWeather().rain_splash_scale;
+ // particle.position = blockerPos + float3(0, splashSize * 0.5, 0);
+ // particle.sizeBeginEnd = splashSize;
+ // particle.life = 0.15;
+ // }
+ // }
+ //}
if (particle.life > 0)
{
diff --git a/WickedEngine/shaders/envMap_skyPS_dynamic.hlsl b/WickedEngine/shaders/envMap_skyPS_dynamic.hlsl
index 00793f124..1059291b4 100644
--- a/WickedEngine/shaders/envMap_skyPS_dynamic.hlsl
+++ b/WickedEngine/shaders/envMap_skyPS_dynamic.hlsl
@@ -1,6 +1,5 @@
#define OBJECTSHADER_USE_NORMAL
#define OBJECTSHADER_USE_RENDERTARGETARRAYINDEX
-#define DISABLE_SOFT_SHADOWMAP
#define TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK
#include "objectHF.hlsli"
#include "lightingHF.hlsli"
diff --git a/WickedEngine/shaders/globals.hlsli b/WickedEngine/shaders/globals.hlsli
index f95c91c01..c98c21449 100644
--- a/WickedEngine/shaders/globals.hlsli
+++ b/WickedEngine/shaders/globals.hlsli
@@ -541,6 +541,9 @@ struct PrimitiveID
#define MEDIUMP_FLT_MAX 65504.0
#define sqr(a) ((a)*(a))
+#define pow2(a) ((a)*(a))
+#define pow3(a) ((a)*(a)*(a))
+#define pow4(a) ((a)*(a)*(a)*(a))
#define pow5(a) ((a)*(a)*(a)*(a)*(a))
#define arraysize(a) (sizeof(a) / sizeof(a[0]))
#define saturateMediump(x) min(x, MEDIUMP_FLT_MAX)
diff --git a/WickedEngine/shaders/lightingHF.hlsli b/WickedEngine/shaders/lightingHF.hlsli
index 5c9f8437e..757f51c58 100644
--- a/WickedEngine/shaders/lightingHF.hlsli
+++ b/WickedEngine/shaders/lightingHF.hlsli
@@ -6,26 +6,10 @@
#include "voxelConeTracingHF.hlsli"
#include "skyHF.hlsli"
-#ifdef CARTOON
-#define DISABLE_SOFT_SHADOWMAP
-#endif // CARTOON
-
#ifdef WATER
#define LIGHTING_SCATTER
#endif // WATER
-template
-inline void QuadBlur(inout T value)
-{
-#if __SHADER_TARGET_STAGE == __SHADER_STAGE_PIXEL && defined(SHADOW_SAMPLING_DISK) && !defined(__spirv__) // Note: Vulkan is disabled because AMD doesn't handle it correctly
-// Average shadow within quad, this smooths out the dithering a bit:
-// Note that I don't implement this in shadowHF.hlsli because we need to
-// make sure that when averaging, all lanes in the quad are coherent
-// It wouldn't be good if some waves are not sampling shadows or sampling different slices
- value = (value + QuadReadAcrossX(value) + QuadReadAcrossY(value) + QuadReadAcrossDiagonal(value)) * 0.25;
-#endif // __SHADER_STAGE_PIXEL
-}
-
struct LightingPart
{
half3 diffuse;
@@ -104,13 +88,11 @@ inline void light_directional(in ShaderEntity light, in Surface surface, inout L
if (cascade_fade > 0 && dither(surface.pixel + GetTemporalAASampleRotation()) < cascade_fade)
continue;
- light_color *= shadow_2D(light, shadow_pos, shadow_uv.xy, cascade, surface.pixel);
+ light_color *= shadow_2D(light, shadow_pos, shadow_uv.xy, cascade);
break;
}
}
}
-
- QuadBlur(light_color);
}
[branch]
@@ -205,10 +187,8 @@ inline void light_point(in ShaderEntity light, in Surface surface, inout Lightin
if ((GetFrame().options & OPTION_BIT_RAYTRACED_SHADOWS) == 0 || GetCamera().texture_rtshadow_index < 0 || (GetCamera().options & SHADERCAMERA_OPTION_USE_SHADOW_MASK) == 0)
#endif // SHADOW_MASK_ENABLED
{
- light_color *= shadow_cube(light, LunnormalizedShadow, surface.pixel);
+ light_color *= shadow_cube(light, LunnormalizedShadow);
}
-
- QuadBlur(light_color);
}
light_color *= attenuation_pointlight(dist2, range, range2);
@@ -305,11 +285,9 @@ inline void light_spot(in ShaderEntity light, in Surface surface, inout Lighting
[branch]
if (is_saturated(shadow_uv))
{
- light_color *= shadow_2D(light, shadow_pos.xyz, shadow_uv.xy, 0, surface.pixel);
+ light_color *= shadow_2D(light, shadow_pos.xyz, shadow_uv.xy, 0);
}
}
-
- QuadBlur(light_color);
}
light_color *= attenuation_spotlight(dist2, range, range2, spot_factor, light.GetAngleScale(), light.GetAngleOffset());
diff --git a/WickedEngine/shaders/objectPS_voxelizer.hlsl b/WickedEngine/shaders/objectPS_voxelizer.hlsl
index 29dd2edc3..f770e8910 100644
--- a/WickedEngine/shaders/objectPS_voxelizer.hlsl
+++ b/WickedEngine/shaders/objectPS_voxelizer.hlsl
@@ -1,4 +1,3 @@
-#define DISABLE_SOFT_SHADOWMAP
#define DISABLE_VOXELGI
#include "objectHF.hlsli"
#include "voxelHF.hlsli"
diff --git a/WickedEngine/shaders/oceanSurfacePS.hlsl b/WickedEngine/shaders/oceanSurfacePS.hlsl
index 7a0ebdbb5..637a0fefc 100644
--- a/WickedEngine/shaders/oceanSurfacePS.hlsl
+++ b/WickedEngine/shaders/oceanSurfacePS.hlsl
@@ -1,7 +1,6 @@
#define DISABLE_DECALS
#define DISABLE_ENVMAPS
#define DISABLE_TRANSPARENT_SHADOWMAP
-#define DISABLE_SOFT_SHADOWMAP
#define TRANSPARENT
#define WATER
#include "globals.hlsli"
diff --git a/WickedEngine/shaders/rtdiffuseCS.hlsl b/WickedEngine/shaders/rtdiffuseCS.hlsl
index f6e4b2e93..d683b1749 100644
--- a/WickedEngine/shaders/rtdiffuseCS.hlsl
+++ b/WickedEngine/shaders/rtdiffuseCS.hlsl
@@ -1,5 +1,4 @@
#define RTAPI
-#define DISABLE_SOFT_SHADOWMAP
#define SURFACE_LOAD_MIPCONE
#define TEXTURE_SLOT_NONUNIFORM
diff --git a/WickedEngine/shaders/rtreflectionCS.hlsl b/WickedEngine/shaders/rtreflectionCS.hlsl
index c34554314..52e0c9837 100644
--- a/WickedEngine/shaders/rtreflectionCS.hlsl
+++ b/WickedEngine/shaders/rtreflectionCS.hlsl
@@ -1,5 +1,4 @@
#define RTAPI
-#define DISABLE_SOFT_SHADOWMAP
#define DISABLE_TRANSPARENT_SHADOWMAP
#define SURFACE_LOAD_MIPCONE
#define TEXTURE_SLOT_NONUNIFORM
diff --git a/WickedEngine/shaders/rtreflectionLIB.hlsl b/WickedEngine/shaders/rtreflectionLIB.hlsl
index 870f9aa30..5408e500b 100644
--- a/WickedEngine/shaders/rtreflectionLIB.hlsl
+++ b/WickedEngine/shaders/rtreflectionLIB.hlsl
@@ -1,5 +1,4 @@
#define RTAPI
-#define DISABLE_SOFT_SHADOWMAP
#define DISABLE_TRANSPARENT_SHADOWMAP
#include "globals.hlsli"
diff --git a/WickedEngine/shaders/shadowHF.hlsli b/WickedEngine/shaders/shadowHF.hlsli
index 6bffa14a7..c227b2241 100644
--- a/WickedEngine/shaders/shadowHF.hlsli
+++ b/WickedEngine/shaders/shadowHF.hlsli
@@ -2,108 +2,86 @@
#define WI_SHADOW_HF
#include "globals.hlsli"
-#define SHADOW_SAMPLING_DISK
+// Moment shadow mapping things from: https://github.com/TheRealMJP/Shadows
+float Linstep(float a, float b, float v)
+{
+ return saturate((v - a) / (b - a));
+}
+float ReduceLightBleeding(float pMax, float amount)
+{
+ // Remove the [0, amount] tail and linearly rescale (amount, 1].
+ return Linstep(amount, 1.0f, pMax);
+}
+float4 ConvertOptimizedMoments(in float4 optimizedMoments)
+{
+ optimizedMoments[0] -= 0.035955884801f;
+ return mul(optimizedMoments, float4x4(0.2227744146f, 0.1549679261f, 0.1451988946f, 0.163127443f,
+ 0.0771972861f, 0.1394629426f, 0.2120202157f, 0.2591432266f,
+ 0.7926986636f, 0.7963415838f, 0.7258694464f, 0.6539092497f,
+ 0.0319417555f,-0.1722823173f,-0.2758014811f,-0.3376131734f));
+}
+float ComputeMSMHamburger(in float4 moments, in float fragmentDepth , in float depthBias, in float momentBias)
+{
+ // Bias input data to avoid artifacts
+ float4 b = lerp(moments, float4(0.5f, 0.5f, 0.5f, 0.5f), momentBias);
+ float3 z;
+ z[0] = fragmentDepth - depthBias;
-#ifdef SHADOW_SAMPLING_DISK
+ // Compute a Cholesky factorization of the Hankel matrix B storing only non-
+ // trivial entries or related products
+ float L32D22 = mad(-b[0], b[1], b[2]);
+ float D22 = mad(-b[0], b[0], b[1]);
+ float squaredDepthVariance = mad(-b[1], b[1], b[3]);
+ float D33D22 = dot(float2(squaredDepthVariance, -L32D22), float2(D22, L32D22));
+ float InvD22 = 1.0f / D22;
+ float L32 = L32D22 * InvD22;
-// "Vogel disk" sampling pattern based on: https://github.com/corporateshark/poisson-disk-generator/blob/master/PoissonGenerator.h
-// Baked values are remapped from [0, 1] range into [-1, 1] range by doing: value * 2 - 1
-static const half2 vogel_points[] = {
- half2(0.353553, 0.000000),
- half2(-0.451560, 0.413635),
- half2(0.069174, -0.787537),
- half2(0.569060, 0.742409),
-};
+ // Obtain a scaled inverse image of bz = (1,z[0],z[0]*z[0])^T
+ float3 c = float3(1.0f, z[0], z[0] * z[0]);
-static const min16uint soft_shadow_sample_count = arraysize(vogel_points);
-static const half soft_shadow_sample_count_rcp = rcp((half)soft_shadow_sample_count);
+ // Forward substitution to solve L*c1=bz
+ c[1] -= b.x;
+ c[2] -= b.y + L32 * c[1];
-inline half3 sample_shadow(float2 uv, float cmp, float4 uv_clamping, half radius, uint2 pixel)
+ // Scaling to solve D*c2=c1
+ c[1] *= InvD22;
+ c[2] *= D22 / D33D22;
+
+ // Backward substitution to solve L^T*c3=c2
+ c[1] -= L32 * c[2];
+ c[0] -= dot(c.yz, b.xy);
+
+ // Solve the quadratic equation c[0]+c[1]*z+c[2]*z^2 to obtain solutions
+ // z[1] and z[2]
+ float p = c[1] / c[2];
+ float q = c[0] / c[2];
+ float D = (p * p * 0.25f) - q;
+ float r = sqrt(D);
+ z[1] =- p * 0.5f - r;
+ z[2] =- p * 0.5f + r;
+
+ // Compute the shadow intensity by summing the appropriate weights
+ float4 switchVal = (z[2] < z[0]) ? float4(z[1], z[0], 1.0f, 1.0f) :
+ ((z[1] < z[0]) ? float4(z[0], z[1], 0.0f, 1.0f) :
+ float4(0.0f,0.0f,0.0f,0.0f));
+ float quotient = (switchVal[0] * z[2] - b[0] * (switchVal[0] + z[2]) + b[1])/((z[2] - switchVal[1]) * (z[0] - z[1]));
+ float shadowIntensity = switchVal[2] + switchVal[3] * quotient;
+ return 1.0f - saturate(shadowIntensity);
+}
+
+static const float MSMDepthBias = 0.0;
+static const float MSMMomentBias = 0.01;
+static const float LightBleedingReduction = 0.16;
+
+inline half3 sample_shadow(float2 uv, float cmp)
{
Texture2D texture_shadowatlas = bindless_textures[GetFrame().texture_shadowatlas_index];
- Texture2D texture_shadowatlas_transparent = bindless_textures[GetFrame().texture_shadowatlas_transparent_index];
- half3 shadow = 0;
-
-#ifndef DISABLE_SOFT_SHADOWMAP
- const float2 spread = GetFrame().shadow_atlas_resolution_rcp.xy * (mad(radius, 8, 2)); // remap radius to try to match ray traced shadow result
- const half2x2 rot = dither_rot2x2(pixel + GetTemporalAASampleRotation()); // per pixel rotation for every sample
- for (min16uint i = 0; i < soft_shadow_sample_count; ++i)
- {
- float2 sample_uv = mad(mul(vogel_points[i], rot), spread, uv);
-#else
- float2 sample_uv = uv;
-#endif // DISABLE_SOFT_SHADOWMAP
-
- sample_uv = clamp(sample_uv, uv_clamping.xy, uv_clamping.zw);
- half3 pcf = texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, sample_uv, cmp).rrr;
-
-#ifndef DISABLE_TRANSPARENT_SHADOWMAP
- half4 transparent_shadow = texture_shadowatlas_transparent.SampleLevel(sampler_linear_clamp, sample_uv, 0);
-#ifdef TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK
- if (transparent_shadow.a > cmp)
-#endif // TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK
- {
- pcf *= transparent_shadow.rgb;
- }
-#endif // DISABLE_TRANSPARENT_SHADOWMAP
-
- shadow += pcf;
-
-#ifndef DISABLE_SOFT_SHADOWMAP
- }
- shadow *= soft_shadow_sample_count_rcp;
-#endif // DISABLE_SOFT_SHADOWMAP
-
- return shadow;
-}
-
-// This is used to clamp the uvs to last texel center to avoid sampling on the border and overfiltering into a different shadow
-inline float4 shadow_border_clamp(in ShaderEntity light, in float slice)
-{
- const float border_size = 0.75 * GetFrame().shadow_atlas_resolution_rcp;
- const float2 topleft = mad(float2(slice, 0), light.shadowAtlasMulAdd.xy, light.shadowAtlasMulAdd.zw) + border_size;
- const float2 bottomright = mad(float2(slice + 1, 1), light.shadowAtlasMulAdd.xy, light.shadowAtlasMulAdd.zw) - border_size;
- return float4(topleft, bottomright);
-}
-
-inline half3 shadow_2D(in ShaderEntity light, in float3 shadow_pos, in float2 shadow_uv, in uint cascade, uint2 pixel = 0)
-{
- shadow_uv.x += cascade;
- shadow_uv = mad(shadow_uv, light.shadowAtlasMulAdd.xy, light.shadowAtlasMulAdd.zw);
- return sample_shadow(shadow_uv, shadow_pos.z, shadow_border_clamp(light, cascade), light.GetRadius(), pixel);
-}
-
-inline half3 shadow_cube(in ShaderEntity light, in float3 Lunnormalized, uint2 pixel = 0)
-{
- const float remapped_distance = light.GetCubemapDepthRemapNear() + light.GetCubemapDepthRemapFar() / (max(max(abs(Lunnormalized.x), abs(Lunnormalized.y)), abs(Lunnormalized.z)) * 0.989); // little bias to avoid artifact
- const float3 uv_slice = cubemap_to_uv(-Lunnormalized);
- float2 shadow_uv = uv_slice.xy;
- shadow_uv.x += uv_slice.z;
- shadow_uv = mad(shadow_uv, light.shadowAtlasMulAdd.xy, light.shadowAtlasMulAdd.zw);
- return sample_shadow(shadow_uv, remapped_distance, shadow_border_clamp(light, uv_slice.z), light.GetRadius(), pixel);
-}
-
-#else
-
-inline half3 sample_shadow(float2 uv, float cmp, uint2 pixel)
-{
- Texture2D texture_shadowatlas = bindless_textures[GetFrame().texture_shadowatlas_index];
- half3 shadow = (half)texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, uv, cmp).r;
-
-#ifndef DISABLE_SOFT_SHADOWMAP
- // sample along a rectangle pattern around center:
- shadow.x += (half)texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, uv, cmp, int2(-1, -1)).r;
- shadow.x += (half)texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, uv, cmp, int2(-1, 0)).r;
- shadow.x += (half)texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, uv, cmp, int2(-1, 1)).r;
- shadow.x += (half)texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, uv, cmp, int2(0, -1)).r;
- shadow.x += (half)texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, uv, cmp, int2(0, 1)).r;
- shadow.x += (half)texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, uv, cmp, int2(1, -1)).r;
- shadow.x += (half)texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, uv, cmp, int2(1, 0)).r;
- shadow.x += (half)texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, uv, cmp, int2(1, 1)).r;
- shadow = shadow.xxx / 9.0;
-#endif // DISABLE_SOFT_SHADOWMAP
-
+ float4 moments = texture_shadowatlas.SampleLevel(sampler_linear_clamp, uv, 0);
+ moments = ConvertOptimizedMoments(moments); // 16-bit moments fix
+ float msm = ComputeMSMHamburger(moments, 1 - cmp, MSMDepthBias * 0.001f, MSMMomentBias * 0.001f);
+ half3 shadow = ReduceLightBleeding(msm, LightBleedingReduction);
+
#ifndef DISABLE_TRANSPARENT_SHADOWMAP
Texture2D texture_shadowatlas_transparent = bindless_textures[GetFrame().texture_shadowatlas_transparent_index];
half4 transparent_shadow = (half4)texture_shadowatlas_transparent.SampleLevel(sampler_linear_clamp, uv, 0);
@@ -122,11 +100,7 @@ inline half3 sample_shadow(float2 uv, float cmp, uint2 pixel)
inline void shadow_border_shrink(in ShaderEntity light, inout float2 shadow_uv)
{
const float2 shadow_resolution = light.shadowAtlasMulAdd.xy * GetFrame().shadow_atlas_resolution;
-#ifdef DISABLE_SOFT_SHADOWMAP
const float border_size = 0.5;
-#else
- const float border_size = 1.5;
-#endif // DISABLE_SOFT_SHADOWMAP
shadow_uv = clamp(shadow_uv * shadow_resolution, border_size, shadow_resolution - border_size) / shadow_resolution;
}
@@ -135,10 +109,10 @@ inline half3 shadow_2D(in ShaderEntity light, in float3 shadow_pos, in float2 sh
shadow_border_shrink(light, shadow_uv);
shadow_uv.x += cascade;
shadow_uv = mad(shadow_uv, light.shadowAtlasMulAdd.xy, light.shadowAtlasMulAdd.zw);
- return sample_shadow(shadow_uv, shadow_pos.z, pixel);
+ return sample_shadow(shadow_uv, shadow_pos.z);
}
-inline half3 shadow_cube(in ShaderEntity light, in float3 Lunnormalized, in uint2 pixel = 0)
+inline half3 shadow_cube(in ShaderEntity light, in float3 Lunnormalized)
{
const float remapped_distance = light.GetCubemapDepthRemapNear() + light.GetCubemapDepthRemapFar() / (max(max(abs(Lunnormalized.x), abs(Lunnormalized.y)), abs(Lunnormalized.z)) * 0.989); // little bias to avoid artifact
const float3 uv_slice = cubemap_to_uv(-Lunnormalized);
@@ -146,11 +120,9 @@ inline half3 shadow_cube(in ShaderEntity light, in float3 Lunnormalized, in uint
shadow_border_shrink(light, shadow_uv);
shadow_uv.x += uv_slice.z;
shadow_uv = mad(shadow_uv, light.shadowAtlasMulAdd.xy, light.shadowAtlasMulAdd.zw);
- return sample_shadow(shadow_uv, remapped_distance, pixel);
+ return sample_shadow(shadow_uv, remapped_distance);
}
-#endif // SHADOW_SAMPLING_DISK
-
inline half shadow_2D_volumetricclouds(float3 P)
{
// Project into shadow map space (no need to divide by .w because ortho projection!):
@@ -217,18 +189,17 @@ inline bool rain_blocker_check(in float3 P)
}
[branch]
- if (GetFrame().texture_shadowatlas_index < 0 || !any(GetFrame().rain_blocker_mad))
+ if (!any(GetFrame().rain_blocker_mad))
return false;
- Texture2D texture_shadowatlas = bindless_textures[GetFrame().texture_shadowatlas_index];
float3 shadow_pos = mul(GetFrame().rain_blocker_matrix, float4(P, 1)).xyz;
float3 shadow_uv = clipspace_to_uv(shadow_pos);
if (is_saturated(shadow_uv))
{
shadow_uv.xy = mad(shadow_uv.xy, GetFrame().rain_blocker_mad.xy, GetFrame().rain_blocker_mad.zw);
- float shadow = texture_shadowatlas.SampleLevel(sampler_point_clamp, shadow_uv.xy, 0).r;
+ float shadow = sample_shadow(shadow_uv.xy, shadow_pos.z).r;
- if(shadow > shadow_pos.z)
+ if(shadow < 0.5)
{
return true;
}
@@ -250,18 +221,17 @@ inline bool rain_blocker_check_prev(in float3 P)
}
[branch]
- if (GetFrame().texture_shadowatlas_index < 0 || !any(GetFrame().rain_blocker_mad_prev))
+ if (!any(GetFrame().rain_blocker_mad_prev))
return false;
- Texture2D texture_shadowatlas = bindless_textures[GetFrame().texture_shadowatlas_index];
float3 shadow_pos = mul(GetFrame().rain_blocker_matrix_prev, float4(P, 1)).xyz;
float3 shadow_uv = clipspace_to_uv(shadow_pos);
if (is_saturated(shadow_uv))
{
shadow_uv.xy = mad(shadow_uv.xy, GetFrame().rain_blocker_mad_prev.xy, GetFrame().rain_blocker_mad_prev.zw);
- float shadow = texture_shadowatlas.SampleLevel(sampler_point_clamp, shadow_uv.xy, 0).r;
-
- if(shadow > shadow_pos.z)
+ float shadow = sample_shadow(shadow_uv.xy, shadow_pos.z).r;
+
+ if(shadow < 0.5)
{
return true;
}
diff --git a/WickedEngine/shaders/shadow_filterCS.hlsl b/WickedEngine/shaders/shadow_filterCS.hlsl
new file mode 100644
index 000000000..2c43e416c
--- /dev/null
+++ b/WickedEngine/shaders/shadow_filterCS.hlsl
@@ -0,0 +1,61 @@
+#include "globals.hlsli"
+
+PUSHCONSTANT(push, ShadowFilterPush);
+
+Texture2D shadowAtlas : register(t0);
+
+RWTexture2D shadowAtlas_filtered : register(u0);
+
+static const uint soft_shadow_sample_count = 16;
+static const float soft_shadow_sample_count_rcp = 1.0 / (float)soft_shadow_sample_count;
+static const float soft_shadow_sample_count_4_rcp = 1.0 / (float)(soft_shadow_sample_count * 4);
+static const float soft_shadow_sample_count_sqrt_rcp = rsqrt((float)soft_shadow_sample_count);
+static const float kGoldenAngle = 2.4;
+
+// Moment shadow mapping things from: https://github.com/TheRealMJP/Shadows
+float4 GetOptimizedMoments(in float depth)
+{
+ float square = depth * depth;
+ float4 moments = float4(depth, square, square * depth, square * square);
+ float4 optimized = mul(moments, float4x4(-2.07224649f, 13.7948857237f, 0.105877704f, 9.7924062118f,
+ 32.23703778f, -59.4683975703f, -1.9077466311f, -33.7652110555f,
+ -68.571074599f, 82.0359750338f, 9.3496555107f, 47.9456096605f,
+ 39.3703274134f,-35.364903257f, -6.6543490743f, -23.9728048165f));
+ optimized[0] += 0.035955884801f;
+ return optimized;
+}
+
+[numthreads(8, 8, 1)]
+void main(uint3 DTid : SV_DispatchThreadID)
+{
+ const uint2 pixel = clamp(DTid.xy, 0, push.rect.zw) + push.rect.xy;
+ const float2 uv = (pixel + 0.5) * push.atlas_resolution_rcp;
+
+ const float border = 0.51;
+ const float2 topleft = (push.rect.xy + border) * push.atlas_resolution_rcp;
+ const float2 bottomright = (push.rect.xy + push.rect.zw - border) * push.atlas_resolution_rcp;
+
+ float4 moments = 0;
+
+ const float2 spread_offset = push.atlas_resolution_rcp * (2 + push.spread * 8);
+ for (uint i = 0; i < soft_shadow_sample_count; ++i)
+ {
+ // "Vogel disk" sampling pattern based on: https://github.com/corporateshark/poisson-disk-generator/blob/master/PoissonGenerator.h
+ const float r = sqrt(float(i) + 0.5) * soft_shadow_sample_count_sqrt_rcp;
+ const float theta = i * kGoldenAngle;
+ float2 theta_cos_sin;
+ sincos(theta, theta_cos_sin.y, theta_cos_sin.x);
+ const float2 sample_uv = clamp(uv + r * theta_cos_sin * spread_offset, topleft, bottomright);
+ const float4 depths = 1 - shadowAtlas.GatherRed(sampler_point_clamp, sample_uv).r;
+ moments += GetOptimizedMoments(depths.w);
+ moments += GetOptimizedMoments(depths.z);
+ moments += GetOptimizedMoments(depths.x);
+ moments += GetOptimizedMoments(depths.y);
+ }
+ moments *= soft_shadow_sample_count_4_rcp;
+
+ if(all(DTid.xy < push.rect.zw))
+ {
+ shadowAtlas_filtered[pixel] = moments;
+ }
+}
diff --git a/WickedEngine/shaders/skyAtmosphere_cameraVolumeLutCS.hlsl b/WickedEngine/shaders/skyAtmosphere_cameraVolumeLutCS.hlsl
index 095e0704b..778d0a626 100644
--- a/WickedEngine/shaders/skyAtmosphere_cameraVolumeLutCS.hlsl
+++ b/WickedEngine/shaders/skyAtmosphere_cameraVolumeLutCS.hlsl
@@ -1,4 +1,3 @@
-#define DISABLE_SOFT_SHADOWMAP
#define TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK
#include "globals.hlsli"
#include "skyAtmosphere.hlsli"
diff --git a/WickedEngine/shaders/skyPS_dynamic.hlsl b/WickedEngine/shaders/skyPS_dynamic.hlsl
index eb9f391f1..1753932b2 100644
--- a/WickedEngine/shaders/skyPS_dynamic.hlsl
+++ b/WickedEngine/shaders/skyPS_dynamic.hlsl
@@ -1,4 +1,3 @@
-#define DISABLE_SOFT_SHADOWMAP
#define TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK
#include "objectHF.hlsli"
#include "skyHF.hlsli"
diff --git a/WickedEngine/shaders/visibility_shadeCS.hlsl b/WickedEngine/shaders/visibility_shadeCS.hlsl
index c6eafe942..9a9515fa1 100644
--- a/WickedEngine/shaders/visibility_shadeCS.hlsl
+++ b/WickedEngine/shaders/visibility_shadeCS.hlsl
@@ -3,7 +3,6 @@
#define DISABLE_DECALS // decals were applied in surface shader
//#define DISABLE_VOXELGI
//#define DISABLE_ENVMAPS
-//#define DISABLE_SOFT_SHADOWMAP
//#define DISABLE_TRANSPARENT_SHADOWMAP
#ifdef PLANARREFLECTION
diff --git a/WickedEngine/shaders/volumetricCloud_renderCS.hlsl b/WickedEngine/shaders/volumetricCloud_renderCS.hlsl
index 67cf4d175..91890dbb9 100644
--- a/WickedEngine/shaders/volumetricCloud_renderCS.hlsl
+++ b/WickedEngine/shaders/volumetricCloud_renderCS.hlsl
@@ -1,4 +1,3 @@
-#define DISABLE_SOFT_SHADOWMAP
#define TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK
#include "globals.hlsli"
#include "volumetricCloudsHF.hlsli"
diff --git a/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl b/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl
index 5ce41595b..10843dcec 100644
--- a/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl
+++ b/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl
@@ -1,5 +1,4 @@
#define TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK // fix the lack of depth testing
-#define DISABLE_SOFT_SHADOWMAP
#include "volumetricLightHF.hlsli"
#include "volumetricCloudsHF.hlsli"
#include "fogHF.hlsli"
@@ -59,7 +58,7 @@ float4 main(VertexToPixel input) : SV_TARGET
[branch]
if (is_saturated(shadow_uv))
{
- shadow *= shadow_2D(light, shadow_pos, shadow_uv.xy, cascade, input.pos.xy);
+ shadow *= shadow_2D(light, shadow_pos, shadow_uv.xy, cascade);
break;
}
}
diff --git a/WickedEngine/shaders/volumetricLight_PointPS.hlsl b/WickedEngine/shaders/volumetricLight_PointPS.hlsl
index 347ed77a6..e5f59f59f 100644
--- a/WickedEngine/shaders/volumetricLight_PointPS.hlsl
+++ b/WickedEngine/shaders/volumetricLight_PointPS.hlsl
@@ -1,5 +1,4 @@
#define TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK // fix the lack of depth testing
-#define DISABLE_SOFT_SHADOWMAP
#include "volumetricLightHF.hlsli"
#include "fogHF.hlsli"
#include "oceanSurfaceHF.hlsli"
@@ -63,7 +62,7 @@ float4 main(VertexToPixel input) : SV_TARGET
[branch]
if (light.IsCastingShadow())
{
- attenuation *= shadow_cube(light, Lunnormalized, input.pos.xy);
+ attenuation *= shadow_cube(light, Lunnormalized);
}
// Evaluate sample height for height fog calculation, given 0 for V:
diff --git a/WickedEngine/shaders/volumetricLight_SpotPS.hlsl b/WickedEngine/shaders/volumetricLight_SpotPS.hlsl
index 0ce908d34..eaf5e9f84 100644
--- a/WickedEngine/shaders/volumetricLight_SpotPS.hlsl
+++ b/WickedEngine/shaders/volumetricLight_SpotPS.hlsl
@@ -1,5 +1,4 @@
#define TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK // fix the lack of depth testing
-#define DISABLE_SOFT_SHADOWMAP
#include "volumetricLightHF.hlsli"
#include "fogHF.hlsli"
#include "oceanSurfaceHF.hlsli"
@@ -112,7 +111,7 @@ float4 main(VertexToPixel input) : SV_TARGET
[branch]
if ((saturate(shadow_uv.x) == shadow_uv.x) && (saturate(shadow_uv.y) == shadow_uv.y))
{
- attenuation *= shadow_2D(light, shadow_pos.xyz, shadow_uv.xy, 0, input.pos.xy);
+ attenuation *= shadow_2D(light, shadow_pos.xyz, shadow_uv.xy, 0);
}
}
diff --git a/WickedEngine/shaders/wetmap_updateCS.hlsl b/WickedEngine/shaders/wetmap_updateCS.hlsl
index 97faee9cc..52a7c77cd 100644
--- a/WickedEngine/shaders/wetmap_updateCS.hlsl
+++ b/WickedEngine/shaders/wetmap_updateCS.hlsl
@@ -1,4 +1,5 @@
#include "globals.hlsli"
+#include "shadowHF.hlsli"
PUSHCONSTANT(push, WetmapPush);
@@ -50,26 +51,15 @@ void main(uint DTid : SV_DispatchThreadID)
}
}
- if(push.rain_amount > 0 && GetFrame().texture_shadowatlas_index >= 0 && any(GetFrame().rain_blocker_mad))
+ if(push.rain_amount > 0 && any(GetFrame().rain_blocker_mad))
{
- Texture2D texture_shadowatlas = bindless_textures[GetFrame().texture_shadowatlas_index];
float3 shadow_pos = mul(GetFrame().rain_blocker_matrix, float4(world_pos, 1)).xyz;
float3 shadow_uv = clipspace_to_uv(shadow_pos);
float shadow = 1;
if (is_saturated(shadow_uv))
{
shadow_uv.xy = mad(shadow_uv.xy, GetFrame().rain_blocker_mad.xy, GetFrame().rain_blocker_mad.zw);
-
- float cmp = shadow_pos.z + 0.001;
- shadow = texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, shadow_uv.xy, cmp, 2 * int2(-1, -1)).r;
- shadow += texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, shadow_uv.xy, cmp, 2 * int2(-1, 0)).r;
- shadow += texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, shadow_uv.xy, cmp, 2 * int2(-1, 1)).r;
- shadow += texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, shadow_uv.xy, cmp, 2 * int2(0, -1)).r;
- shadow += texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, shadow_uv.xy, cmp, 2 * int2(0, 1)).r;
- shadow += texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, shadow_uv.xy, cmp, 2 * int2(1, -1)).r;
- shadow += texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, shadow_uv.xy, cmp, 2 * int2(1, 0)).r;
- shadow += texture_shadowatlas.SampleCmpLevelZero(sampler_cmp_depth, shadow_uv.xy, cmp, 2 * int2(1, 1)).r;
- shadow /= 9.0;
+ shadow = sample_shadow(shadow_uv, shadow_pos.z);
}
if(shadow == 0)
diff --git a/WickedEngine/wiEnums.h b/WickedEngine/wiEnums.h
index 99f891b2b..e9b950a5b 100644
--- a/WickedEngine/wiEnums.h
+++ b/WickedEngine/wiEnums.h
@@ -413,6 +413,7 @@ namespace wi::enums
CSTYPE_YUV_TO_RGB,
CSTYPE_WETMAP_UPDATE,
CSTYPE_CAUSTICS,
+ CSTYPE_SHADOW_FILTER,
// raytracing pipelines:
diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp
index 8d64571a9..d9623b474 100644
--- a/WickedEngine/wiRenderer.cpp
+++ b/WickedEngine/wiRenderer.cpp
@@ -146,6 +146,7 @@ int VXGI_DEBUG_CLIPMAP = 0;
Texture shadowMapAtlas;
Texture shadowMapAtlas_Transparent;
+Texture shadowMapAtlas_Filtered;
int max_shadow_resolution_2D = 1024;
int max_shadow_resolution_cube = 256;
@@ -1143,6 +1144,7 @@ void LoadShaders()
wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_YUV_TO_RGB], "yuv_to_rgbCS.cso"); });
wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_WETMAP_UPDATE], "wetmap_updateCS.cso"); });
wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_CAUSTICS], "causticsCS.cso"); });
+ wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_SHADOW_FILTER], "shadow_filterCS.cso"); });
wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::HS, shaders[HSTYPE_OBJECT], "objectHS.cso"); });
wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::HS, shaders[HSTYPE_OBJECT_PREPASS], "objectHS_prepass.cso"); });
@@ -2044,6 +2046,11 @@ void LoadBuffers()
device->CreateTexture(&desc, nullptr, &shadowMapAtlas);
device->SetName(&shadowMapAtlas, "shadowMapAtlas");
+ desc.format = format_texture_shadowmap_filtered;
+ desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADER_RESOURCE;
+ device->CreateTexture(&desc, nullptr, &shadowMapAtlas_Filtered);
+ device->SetName(&shadowMapAtlas_Filtered, "shadowMapAtlas_Filtered");
+
desc.format = format_rendertarget_shadowmap;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE;
desc.layout = ResourceState::SHADER_RESOURCE;
@@ -2074,10 +2081,10 @@ void SetUpStates()
rs.fill_mode = FillMode::SOLID;
rs.cull_mode = CullMode::BACK;
rs.front_counter_clockwise = true;
- rs.depth_bias = -1;
+ rs.depth_bias = 0;
rs.depth_bias_clamp = 0;
- rs.slope_scaled_depth_bias = -4.0f;
- rs.depth_clip_enable = true;
+ rs.slope_scaled_depth_bias = 0;
+ rs.depth_clip_enable = false; // to not clip ortho shadow near plane
rs.multisample_enable = false;
rs.antialiased_line_enable = false;
rs.conservative_rasterization_enable = false;
@@ -2668,16 +2675,19 @@ inline void CreateDirLightShadowCams(const LightComponent& light, CameraComponen
XMStoreFloat3(&_min, vMin);
XMStoreFloat3(&_max, vMax);
- // Extrude bounds to avoid early shadow clipping:
+ // This is not extruded projection, the GPU depth clipping will be turned off!
+ XMMATRIX lightProjection = XMMatrixOrthographicOffCenterLH(_min.x, _max.x, _min.y, _max.y, _max.z, _min.z); // notice reversed Z!
+ shcams[cascade].view_projection = XMMatrixMultiply(lightView, lightProjection);
+
+ // Extrude bounds to avoid early shadow culling:
float ext = abs(_center.z - _min.z);
ext = std::max(ext, std::min(1500.0f, farPlane) * 0.5f);
_min.z = _center.z - ext;
_max.z = _center.z + ext;
- const XMMATRIX lightProjection = XMMatrixOrthographicOffCenterLH(_min.x, _max.x, _min.y, _max.y, _max.z, _min.z); // notice reversed Z!
-
- shcams[cascade].view_projection = XMMatrixMultiply(lightView, lightProjection);
- shcams[cascade].frustum.Create(shcams[cascade].view_projection);
+ lightProjection = XMMatrixOrthographicOffCenterLH(_min.x, _max.x, _min.y, _max.y, _max.z, _min.z); // notice reversed Z!
+ XMMATRIX culling_view_projection = XMMatrixMultiply(lightView, lightProjection);
+ shcams[cascade].frustum.Create(culling_view_projection);
}
}
@@ -3559,6 +3569,11 @@ void UpdateVisibility(Visibility& vis)
device->CreateTexture(&desc, nullptr, &shadowMapAtlas);
device->SetName(&shadowMapAtlas, "shadowMapAtlas");
+ desc.format = format_texture_shadowmap_filtered;
+ desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADER_RESOURCE;
+ device->CreateTexture(&desc, nullptr, &shadowMapAtlas_Filtered);
+ device->SetName(&shadowMapAtlas_Filtered, "shadowMapAtlas_Filtered");
+
desc.format = format_rendertarget_shadowmap;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE;
desc.layout = ResourceState::SHADER_RESOURCE;
@@ -3846,10 +3861,10 @@ void UpdatePerFrameData(
frameCB.texture_wind_prev_index = device->GetDescriptorIndex(&textures[TEXTYPE_3D_WIND_PREV], SubresourceType::SRV);
frameCB.texture_caustics_index = device->GetDescriptorIndex(&textures[TEXTYPE_2D_CAUSTICS], SubresourceType::SRV);
- frameCB.texture_shadowatlas_index = device->GetDescriptorIndex(&shadowMapAtlas, SubresourceType::SRV);
+ frameCB.texture_shadowatlas_index = device->GetDescriptorIndex(&shadowMapAtlas_Filtered, SubresourceType::SRV);
frameCB.texture_shadowatlas_transparent_index = device->GetDescriptorIndex(&shadowMapAtlas_Transparent, SubresourceType::SRV);
- frameCB.shadow_atlas_resolution.x = shadowMapAtlas.desc.width;
- frameCB.shadow_atlas_resolution.y = shadowMapAtlas.desc.height;
+ frameCB.shadow_atlas_resolution.x = shadowMapAtlas_Filtered.desc.width;
+ frameCB.shadow_atlas_resolution.y = shadowMapAtlas_Filtered.desc.height;
frameCB.shadow_atlas_resolution_rcp.x = 1.0f / frameCB.shadow_atlas_resolution.x;
frameCB.shadow_atlas_resolution_rcp.y = 1.0f / frameCB.shadow_atlas_resolution.y;
@@ -6229,6 +6244,140 @@ void DrawShadowmaps(
device->RenderPassEnd(cmd);
+ device->EventBegin("Shadow Filtering", cmd);
+ device->Barrier(GPUBarrier::Image(&shadowMapAtlas_Filtered, ResourceState::UNDEFINED, ResourceState::UNORDERED_ACCESS), cmd);
+ device->BindComputeShader(&shaders[CSTYPE_SHADOW_FILTER], cmd);
+ device->BindResource(&shadowMapAtlas, 0, cmd);
+ device->BindUAV(&shadowMapAtlas_Filtered, 0, cmd);
+ ShadowFilterPush push = {};
+ push.atlas_resolution_rcp.x = 1.0f / float(shadowMapAtlas.desc.width);
+ push.atlas_resolution_rcp.y = 1.0f / float(shadowMapAtlas.desc.height);
+ for (uint32_t lightIndex : vis.visibleLights)
+ {
+ const LightComponent& light = vis.scene->lights[lightIndex];
+ if (light.IsInactive())
+ continue;
+
+ const bool shadow = light.IsCastingShadow() && !light.IsStatic();
+ if (!shadow)
+ continue;
+ const wi::rectpacker::Rect& shadow_rect = vis.visibleLightShadowRects[lightIndex];
+
+ switch (light.GetType())
+ {
+ case LightComponent::DIRECTIONAL:
+ {
+ if (max_shadow_resolution_2D == 0 && light.forced_shadow_resolution < 0)
+ break;
+ if (light.cascade_distances.empty())
+ break;
+
+ const uint32_t cascade_count = std::min((uint32_t)light.cascade_distances.size(), max_viewport_count);
+ for (uint32_t cascade = 0; cascade < cascade_count; ++cascade)
+ {
+ push.rect.x = shadow_rect.x + cascade * shadow_rect.w;
+ push.rect.y = shadow_rect.y;
+ push.rect.z = shadow_rect.w;
+ push.rect.w = shadow_rect.h;
+ push.spread = light.radius;
+ device->PushConstants(&push, sizeof(push), cmd);
+ device->Dispatch((push.rect.z + 7u) / 8u, (push.rect.w + 7u) / 8u, 1, cmd);
+ }
+ }
+ break;
+ case LightComponent::SPOT:
+ {
+ if (max_shadow_resolution_2D == 0 && light.forced_shadow_resolution < 0)
+ break;
+
+ SHCAM shcam;
+ CreateSpotLightShadowCam(light, shcam);
+ if (!cam_frustum.Intersects(shcam.boundingfrustum))
+ break;
+
+ if (predicationRequest && light.occlusionquery >= 0)
+ {
+ device->PredicationBegin(
+ &vis.scene->queryPredicationBuffer,
+ (uint64_t)light.occlusionquery * sizeof(uint64_t),
+ PredicationOp::EQUAL_ZERO,
+ cmd
+ );
+ }
+
+ push.rect.x = shadow_rect.x;
+ push.rect.y = shadow_rect.y;
+ push.rect.z = shadow_rect.w;
+ push.rect.w = shadow_rect.h;
+ push.spread = light.radius;
+ device->PushConstants(&push, sizeof(push), cmd);
+ device->Dispatch((push.rect.z + 7u) / 8u, (push.rect.w + 7u) / 8u, 1, cmd);
+
+ if (predicationRequest && light.occlusionquery >= 0)
+ {
+ device->PredicationEnd(cmd);
+ }
+ }
+ break;
+ case LightComponent::POINT:
+ {
+ if (max_shadow_resolution_cube == 0 && light.forced_shadow_resolution < 0)
+ break;
+
+ Sphere boundingsphere(light.position, light.GetRange());
+
+ const float zNearP = 0.1f;
+ const float zFarP = std::max(1.0f, light.GetRange());
+ SHCAM cameras[6];
+ CreateCubemapCameras(light.position, zNearP, zFarP, cameras, arraysize(cameras));
+
+ if (predicationRequest && light.occlusionquery >= 0)
+ {
+ device->PredicationBegin(
+ &vis.scene->queryPredicationBuffer,
+ (uint64_t)light.occlusionquery * sizeof(uint64_t),
+ PredicationOp::EQUAL_ZERO,
+ cmd
+ );
+ }
+
+ for (uint32_t shcam = 0; shcam < arraysize(cameras); ++shcam)
+ {
+ // Check if cubemap face frustum is visible from main camera, otherwise, it will be skipped:
+ if (cam_frustum.Intersects(cameras[shcam].boundingfrustum))
+ {
+ push.rect.x = shadow_rect.x + shcam * shadow_rect.w;
+ push.rect.y = shadow_rect.y;
+ push.rect.z = shadow_rect.w;
+ push.rect.w = shadow_rect.h;
+ push.spread = light.radius;
+ device->PushConstants(&push, sizeof(push), cmd);
+ device->Dispatch((push.rect.z + 7u) / 8u, (push.rect.w + 7u) / 8u, 1, cmd);
+ }
+ }
+
+ if (predicationRequest && light.occlusionquery >= 0)
+ {
+ device->PredicationEnd(cmd);
+ }
+ }
+ break;
+ } // terminate switch
+ }
+ // Rain blocker filtering:
+ if (vis.scene->weather.rain_amount > 0)
+ {
+ push.rect.x = vis.rain_blocker_shadow_rect.x;
+ push.rect.y = vis.rain_blocker_shadow_rect.y;
+ push.rect.z = vis.rain_blocker_shadow_rect.w;
+ push.rect.w = vis.rain_blocker_shadow_rect.h;
+ push.spread = 0.5f;
+ device->PushConstants(&push, sizeof(push), cmd);
+ device->Dispatch((push.rect.z + 7u) / 8u, (push.rect.w + 7u) / 8u, 1, cmd);
+ }
+ device->Barrier(GPUBarrier::Image(&shadowMapAtlas_Filtered, ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), cmd);
+ device->EventEnd(cmd);
+
wi::profiler::EndRange(range_gpu);
wi::profiler::EndRange(range_cpu);
device->EventEnd(cmd);
diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h
index 155b5258f..c7c5e5964 100644
--- a/WickedEngine/wiRenderer.h
+++ b/WickedEngine/wiRenderer.h
@@ -29,6 +29,7 @@ namespace wi::renderer
constexpr wi::graphics::Format format_idbuffer = wi::graphics::Format::R32_UINT;
constexpr wi::graphics::Format format_rendertarget_shadowmap = wi::graphics::Format::R16G16B16A16_FLOAT;
constexpr wi::graphics::Format format_depthbuffer_shadowmap = wi::graphics::Format::D16_UNORM;
+ constexpr wi::graphics::Format format_texture_shadowmap_filtered = wi::graphics::Format::R16G16B16A16_UNORM;
constexpr wi::graphics::Format format_rendertarget_envprobe = wi::graphics::Format::R11G11B10_FLOAT;
constexpr wi::graphics::Format format_depthbuffer_envprobe = wi::graphics::Format::D16_UNORM;