moment shadow mapping

This commit is contained in:
Turánszki János
2024-08-05 06:44:20 +02:00
parent 5e27c926f8
commit bf064a340e
27 changed files with 354 additions and 202 deletions
@@ -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
@@ -951,6 +951,10 @@
<FxCompile Include="$(MSBuildThisFileDirectory)aerialPerspectiveCS.hlsl" />
<FxCompile Include="$(MSBuildThisFileDirectory)aerialPerspectiveCS_capture.hlsl" />
<FxCompile Include="$(MSBuildThisFileDirectory)aerialPerspectiveCS_capture_MSAA.hlsl" />
<FxCompile Include="$(MSBuildThisFileDirectory)shadow_filterCS.hlsl">
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compute</ShaderType>
<ShaderModel Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4.0</ShaderModel>
</FxCompile>
<FxCompile Include="$(MSBuildThisFileDirectory)skyAtmosphere_cameraVolumeLutCS.hlsl" />
<FxCompile Include="$(MSBuildThisFileDirectory)skyAtmosphere_skyLuminanceLutCS.hlsl">
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Compute</ShaderType>
@@ -1172,6 +1172,9 @@
<FxCompile Include="$(MSBuildThisFileDirectory)causticsCS.hlsl">
<Filter>CS</Filter>
</FxCompile>
<FxCompile Include="$(MSBuildThisFileDirectory)shadow_filterCS.hlsl">
<Filter>CS</Filter>
</FxCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(MSBuildThisFileDirectory)ShaderInterop.h">
@@ -1,4 +1,3 @@
#define DISABLE_SOFT_SHADOWMAP
#define TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK
#include "globals.hlsli"
#include "skyAtmosphere.hlsli"
@@ -1,3 +1,2 @@
#define EMITTEDPARTICLE_LIGHTING
//#define DISABLE_SOFT_SHADOWMAP
#include "emittedparticlePS_soft.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)
{
@@ -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"
+3
View File
@@ -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)
+3 -25
View File
@@ -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<typename T>
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());
@@ -1,4 +1,3 @@
#define DISABLE_SOFT_SHADOWMAP
#define DISABLE_VOXELGI
#include "objectHF.hlsli"
#include "voxelHF.hlsli"
-1
View File
@@ -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"
-1
View File
@@ -1,5 +1,4 @@
#define RTAPI
#define DISABLE_SOFT_SHADOWMAP
#define SURFACE_LOAD_MIPCONE
#define TEXTURE_SLOT_NONUNIFORM
-1
View File
@@ -1,5 +1,4 @@
#define RTAPI
#define DISABLE_SOFT_SHADOWMAP
#define DISABLE_TRANSPARENT_SHADOWMAP
#define SURFACE_LOAD_MIPCONE
#define TEXTURE_SLOT_NONUNIFORM
@@ -1,5 +1,4 @@
#define RTAPI
#define DISABLE_SOFT_SHADOWMAP
#define DISABLE_TRANSPARENT_SHADOWMAP
#include "globals.hlsli"
+83 -113
View File
@@ -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;
}
+61
View File
@@ -0,0 +1,61 @@
#include "globals.hlsli"
PUSHCONSTANT(push, ShadowFilterPush);
Texture2D<float> shadowAtlas : register(t0);
RWTexture2D<float4> 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;
}
}
@@ -1,4 +1,3 @@
#define DISABLE_SOFT_SHADOWMAP
#define TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK
#include "globals.hlsli"
#include "skyAtmosphere.hlsli"
-1
View File
@@ -1,4 +1,3 @@
#define DISABLE_SOFT_SHADOWMAP
#define TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK
#include "objectHF.hlsli"
#include "skyHF.hlsli"
@@ -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
@@ -1,4 +1,3 @@
#define DISABLE_SOFT_SHADOWMAP
#define TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK
#include "globals.hlsli"
#include "volumetricCloudsHF.hlsli"
@@ -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;
}
}
@@ -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:
@@ -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);
}
}
+3 -13
View File
@@ -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)
+1
View File
@@ -413,6 +413,7 @@ namespace wi::enums
CSTYPE_YUV_TO_RGB,
CSTYPE_WETMAP_UPDATE,
CSTYPE_CAUSTICS,
CSTYPE_SHADOW_FILTER,
// raytracing pipelines:
+160 -11
View File
@@ -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);
+1
View File
@@ -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;