diff --git a/Editor/PostprocessWindow.cpp b/Editor/PostprocessWindow.cpp
index 4eeaf0914..a36481eef 100644
--- a/Editor/PostprocessWindow.cpp
+++ b/Editor/PostprocessWindow.cpp
@@ -11,7 +11,7 @@ using namespace wiGraphics;
void PostprocessWindow::Create(EditorComponent* editor)
{
wiWindow::Create("PostProcess Window");
- SetSize(XMFLOAT2(420, 500));
+ SetSize(XMFLOAT2(420, 520));
float x = 150;
float y = 10;
@@ -156,6 +156,36 @@ void PostprocessWindow::Create(EditorComponent* editor)
AddWidget(&raytracedReflectionsCheckBox);
raytracedReflectionsCheckBox.SetEnabled(wiRenderer::GetDevice()->CheckCapability(GRAPHICSDEVICE_CAPABILITY_RAYTRACING));
+ screenSpaceShadowsCheckBox.Create("SS Shadows: ");
+ screenSpaceShadowsCheckBox.SetTooltip("Enable screen space contact shadows. This can add small shadows details to shadow maps in screen space.");
+ screenSpaceShadowsCheckBox.SetSize(XMFLOAT2(hei, hei));
+ screenSpaceShadowsCheckBox.SetPos(XMFLOAT2(x, y += step));
+ screenSpaceShadowsCheckBox.SetCheck(wiRenderer::GetScreenSpaceShadowsEnabled());
+ screenSpaceShadowsCheckBox.OnClick([=](wiEventArgs args) {
+ wiRenderer::SetScreenSpaceShadowsEnabled(args.bValue);
+ });
+ AddWidget(&screenSpaceShadowsCheckBox);
+
+ screenSpaceShadowsRangeSlider.Create(0.1f, 10.0f, 1, 1000, "Range: ");
+ screenSpaceShadowsRangeSlider.SetTooltip("Range of contact shadows");
+ screenSpaceShadowsRangeSlider.SetSize(XMFLOAT2(100, hei));
+ screenSpaceShadowsRangeSlider.SetPos(XMFLOAT2(x + 100, y));
+ screenSpaceShadowsRangeSlider.SetValue((float)editor->renderPath->getScreenSpaceShadowRange());
+ screenSpaceShadowsRangeSlider.OnSlide([=](wiEventArgs args) {
+ editor->renderPath->setScreenSpaceShadowRange(args.fValue);
+ });
+ AddWidget(&screenSpaceShadowsRangeSlider);
+
+ screenSpaceShadowsStepCountSlider.Create(4, 128, 16, 128 - 4, "Sample Count: ");
+ screenSpaceShadowsStepCountSlider.SetTooltip("Sample count of contact shadows. Higher values are better quality but slower.");
+ screenSpaceShadowsStepCountSlider.SetSize(XMFLOAT2(100, hei));
+ screenSpaceShadowsStepCountSlider.SetPos(XMFLOAT2(x + 100, y += step));
+ screenSpaceShadowsStepCountSlider.SetValue((float)editor->renderPath->getScreenSpaceShadowSampleCount());
+ screenSpaceShadowsStepCountSlider.OnSlide([=](wiEventArgs args) {
+ editor->renderPath->setScreenSpaceShadowSampleCount(args.iValue);
+ });
+ AddWidget(&screenSpaceShadowsStepCountSlider);
+
eyeAdaptionCheckBox.Create("EyeAdaption: ");
eyeAdaptionCheckBox.SetTooltip("Enable eye adaption for the overall screen luminance");
eyeAdaptionCheckBox.SetSize(XMFLOAT2(hei, hei));
diff --git a/Editor/PostprocessWindow.h b/Editor/PostprocessWindow.h
index 5ecada2e5..08d622739 100644
--- a/Editor/PostprocessWindow.h
+++ b/Editor/PostprocessWindow.h
@@ -18,6 +18,9 @@ public:
wiSlider aoSampleCountSlider;
wiCheckBox ssrCheckBox;
wiCheckBox raytracedReflectionsCheckBox;
+ wiCheckBox screenSpaceShadowsCheckBox;
+ wiSlider screenSpaceShadowsStepCountSlider;
+ wiSlider screenSpaceShadowsRangeSlider;
wiCheckBox eyeAdaptionCheckBox;
wiCheckBox motionBlurCheckBox;
wiSlider motionBlurStrengthSlider;
diff --git a/WickedEngine/RenderPath3D.cpp b/WickedEngine/RenderPath3D.cpp
index 939f5e77d..4cbaeb76e 100644
--- a/WickedEngine/RenderPath3D.cpp
+++ b/WickedEngine/RenderPath3D.cpp
@@ -151,7 +151,6 @@ void RenderPath3D::ResizeBuffers()
device->CreateTexture(&desc, nullptr, &rtAO);
device->SetName(&rtAO, "rtAO");
}
- if (device->CheckCapability(GRAPHICSDEVICE_CAPABILITY_RAYTRACING))
{
TextureDesc desc;
desc.BindFlags = BIND_SHADER_RESOURCE | BIND_UNORDERED_ACCESS;
@@ -844,6 +843,19 @@ void RenderPath3D::Render() const
RenderSSR(cmd);
+ if (wiRenderer::GetScreenSpaceShadowsEnabled())
+ {
+ wiRenderer::Postprocess_ScreenSpaceShadow(
+ depthBuffer_Copy,
+ rtLinearDepth,
+ entityTiles_Opaque,
+ rtShadow,
+ cmd,
+ getScreenSpaceShadowRange(),
+ getScreenSpaceShadowSampleCount()
+ );
+ }
+
if (wiRenderer::GetRaytracedShadowsEnabled())
{
wiRenderer::Postprocess_RTShadow(
@@ -857,6 +869,7 @@ void RenderPath3D::Render() const
cmd
);
}
+
});
// Opaque scene:
@@ -875,7 +888,7 @@ void RenderPath3D::Render() const
vp.Height = (float)depthBuffer_Main.GetDesc().Height;
device->BindViewports(1, &vp, cmd);
- if (wiRenderer::GetRaytracedShadowsEnabled())
+ if (wiRenderer::GetRaytracedShadowsEnabled() || wiRenderer::GetScreenSpaceShadowsEnabled())
{
device->BindResource(PS, &rtShadow, TEXSLOT_RENDERPATH_RTSHADOW, cmd);
}
diff --git a/WickedEngine/RenderPath3D.h b/WickedEngine/RenderPath3D.h
index ce052a368..7dd1bce16 100644
--- a/WickedEngine/RenderPath3D.h
+++ b/WickedEngine/RenderPath3D.h
@@ -35,6 +35,8 @@ private:
uint32_t aoSampleCount = 16;
float aoPower = 2.0f;
float chromaticAberrationAmount = 2.0f;
+ uint32_t screenSpaceShadowSampleCount = 16;
+ float screenSpaceShadowRange = 1;
AO ao = AO_DISABLED;
bool fxaaEnabled = false;
@@ -179,6 +181,8 @@ public:
constexpr uint32_t getAOSampleCount() const { return aoSampleCount; }
constexpr float getAOPower() const { return aoPower; }
constexpr float getChromaticAberrationAmount() const { return chromaticAberrationAmount; }
+ constexpr uint32_t getScreenSpaceShadowSampleCount() const { return screenSpaceShadowSampleCount; }
+ constexpr float getScreenSpaceShadowRange() const { return screenSpaceShadowRange; }
constexpr bool getAOEnabled() const { return ao != AO_DISABLED; }
constexpr AO getAO() const { return ao; }
@@ -219,6 +223,8 @@ public:
constexpr void setAOSampleCount(uint32_t value) { aoSampleCount = value; }
constexpr void setAOPower(float value) { aoPower = value; }
constexpr void setChromaticAberrationAmount(float value) { chromaticAberrationAmount = value; }
+ constexpr void setScreenSpaceShadowSampleCount(uint32_t value) { screenSpaceShadowSampleCount = value; }
+ constexpr void setScreenSpaceShadowRange(float value) { screenSpaceShadowRange = value; }
constexpr void setAO(AO value) { ao = value; }
constexpr void setSSREnabled(bool value){ ssrEnabled = value; }
diff --git a/WickedEngine/ShaderInterop_Renderer.h b/WickedEngine/ShaderInterop_Renderer.h
index 12aaaebd0..1b2c51785 100644
--- a/WickedEngine/ShaderInterop_Renderer.h
+++ b/WickedEngine/ShaderInterop_Renderer.h
@@ -234,6 +234,7 @@ static const uint OPTION_BIT_SIMPLE_SKY = 1 << 5;
static const uint OPTION_BIT_REALISTIC_SKY = 1 << 6;
static const uint OPTION_BIT_RAYTRACED_SHADOWS = 1 << 7;
static const uint OPTION_BIT_DISABLE_ALBEDO_MAPS = 1 << 8;
+static const uint OPTION_BIT_SHADOW_MASK = 1 << 9;
// ---------- Common Constant buffers: -----------------
diff --git a/WickedEngine/Shaders_SOURCE.vcxitems b/WickedEngine/Shaders_SOURCE.vcxitems
index 6d3821425..462d22364 100644
--- a/WickedEngine/Shaders_SOURCE.vcxitems
+++ b/WickedEngine/Shaders_SOURCE.vcxitems
@@ -869,6 +869,16 @@
Compute
Compute
+
+ Compute
+ Compute
+ Compute
+ Compute
+ Compute
+ Compute
+ Compute
+ Compute
+
Pixel
Pixel
diff --git a/WickedEngine/Shaders_SOURCE.vcxitems.filters b/WickedEngine/Shaders_SOURCE.vcxitems.filters
index 063c3d736..11f00cc93 100644
--- a/WickedEngine/Shaders_SOURCE.vcxitems.filters
+++ b/WickedEngine/Shaders_SOURCE.vcxitems.filters
@@ -956,5 +956,8 @@
DS
+
+ CS
+
\ No newline at end of file
diff --git a/WickedEngine/hairparticlePS.hlsl b/WickedEngine/hairparticlePS.hlsl
index 514f8e6d3..8e40f6103 100644
--- a/WickedEngine/hairparticlePS.hlsl
+++ b/WickedEngine/hairparticlePS.hlsl
@@ -1,6 +1,6 @@
#define DISABLE_DECALS
#define DISABLE_ENVMAPS
-#define RAYTRACED_SHADOWS_ENABLED
+#define SHADOW_MASK_ENABLED
#include "globals.hlsli"
#include "objectHF.hlsli"
#include "hairparticleHF.hlsli"
@@ -30,13 +30,13 @@ GBuffer main(VertexToPixel input)
Lighting lighting;
lighting.create(0, 0, GetAmbient(surface.N), 0);
-#ifdef RAYTRACED_SHADOWS_ENABLED
+#ifdef SHADOW_MASK_ENABLED
[branch]
if (g_xFrame_Options & OPTION_BIT_RAYTRACED_SHADOWS)
{
lighting.shadow_mask = texture_rtshadow[surface.pixel];
}
-#endif // RAYTRACED_SHADOWS_ENABLED
+#endif // SHADOW_MASK_ENABLED
float depth = input.pos.z;
float3 reflection = 0;
diff --git a/WickedEngine/lightingHF.hlsli b/WickedEngine/lightingHF.hlsli
index 00c0c8025..f5c3ff971 100644
--- a/WickedEngine/lightingHF.hlsli
+++ b/WickedEngine/lightingHF.hlsli
@@ -186,21 +186,12 @@ inline void DirectionalLight(in ShaderEntity light, in Surface surface, inout Li
[branch]
if (light.IsCastingShadow())
{
-#ifdef RAYTRACED_SHADOWS_ENABLED
[branch]
if (g_xFrame_Options & OPTION_BIT_RAYTRACED_SHADOWS)
{
-#ifdef RAYTRACING_INLINE
shadow *= shadowTrace(surface, normalize(L), FLT_MAX);
-#else
- uint mask_shift = (lighting.shadow_index % 4) * 8;
- uint mask_bucket = lighting.shadow_index / 4;
- uint mask = (lighting.shadow_mask[mask_bucket] >> mask_shift) & 0xFF;
- shadow = mask / 255.0;
-#endif // RAYTRACING_INLINE
}
else
-#endif // RAYTRACED_SHADOWS_ENABLED
{
// Loop through cascades from closest (smallest) to furthest (biggest)
[loop]
@@ -238,6 +229,17 @@ inline void DirectionalLight(in ShaderEntity light, in Surface surface, inout Li
}
}
}
+
+#ifdef SHADOW_MASK_ENABLED
+ [branch]
+ if (g_xFrame_Options & OPTION_BIT_SHADOW_MASK)
+ {
+ uint mask_shift = (lighting.shadow_index % 4) * 8;
+ uint mask_bucket = lighting.shadow_index / 4;
+ uint mask = (lighting.shadow_mask[mask_bucket] >> mask_shift) & 0xFF;
+ shadow *= mask / 255.0;
+ }
+#endif // SHADOW_MASK_ENABLED
}
[branch]
@@ -260,7 +262,7 @@ inline void DirectionalLight(in ShaderEntity light, in Surface surface, inout Li
}
}
-#ifdef RAYTRACED_SHADOWS_ENABLED
+#ifdef SHADOW_MASK_ENABLED
#ifndef RAYTRACING_INLINE
[branch]
if (light.IsCastingShadow())
@@ -274,7 +276,7 @@ inline void DirectionalLight(in ShaderEntity light, in Surface surface, inout Li
lighting.shadow_index++;
}
#endif // RAYTRACING_INLINE
-#endif // RAYTRACED_SHADOWS_ENABLED
+#endif // SHADOW_MASK_ENABLED
}
inline void PointLight(in ShaderEntity light, in Surface surface, inout Lighting lighting)
{
@@ -300,24 +302,26 @@ inline void PointLight(in ShaderEntity light, in Surface surface, inout Lighting
[branch]
if (light.IsCastingShadow())
{
-#ifdef RAYTRACED_SHADOWS_ENABLED
[branch]
if (g_xFrame_Options & OPTION_BIT_RAYTRACED_SHADOWS)
{
-#ifdef RAYTRACING_INLINE
shadow *= shadowTrace(surface, L, dist);
-#else
- uint mask_shift = (lighting.shadow_index % 4) * 8;
- uint mask_bucket = lighting.shadow_index / 4;
- uint mask = (lighting.shadow_mask[mask_bucket] >> mask_shift) & 0xFF;
- shadow = mask / 255.0;
-#endif // RAYTRACING_INLINE
}
else
-#endif // RAYTRACED_SHADOWS_ENABLED
{
shadow *= shadowCube(light, L, Lunnormalized);
}
+
+#ifdef SHADOW_MASK_ENABLED
+ [branch]
+ if (g_xFrame_Options & OPTION_BIT_SHADOW_MASK)
+ {
+ uint mask_shift = (lighting.shadow_index % 4) * 8;
+ uint mask_bucket = lighting.shadow_index / 4;
+ uint mask = (lighting.shadow_mask[mask_bucket] >> mask_shift) & 0xFF;
+ shadow *= mask / 255.0;
+ }
+#endif // SHADOW_MASK_ENABLED
}
[branch]
@@ -338,7 +342,7 @@ inline void PointLight(in ShaderEntity light, in Surface surface, inout Lighting
}
}
-#ifdef RAYTRACED_SHADOWS_ENABLED
+#ifdef SHADOW_MASK_ENABLED
#ifndef RAYTRACING_INLINE
[branch]
if (light.IsCastingShadow())
@@ -352,7 +356,7 @@ inline void PointLight(in ShaderEntity light, in Surface surface, inout Lighting
lighting.shadow_index++;
}
#endif // RAYTRACING_INLINE
-#endif // RAYTRACED_SHADOWS_ENABLED
+#endif // SHADOW_MASK_ENABLED
}
inline void SpotLight(in ShaderEntity light, in Surface surface, inout Lighting lighting)
{
@@ -383,21 +387,12 @@ inline void SpotLight(in ShaderEntity light, in Surface surface, inout Lighting
[branch]
if (light.IsCastingShadow())
{
-#ifdef RAYTRACED_SHADOWS_ENABLED
[branch]
if (g_xFrame_Options & OPTION_BIT_RAYTRACED_SHADOWS)
{
-#ifdef RAYTRACING_INLINE
shadow *= shadowTrace(surface, L, dist);
-#else
- uint mask_shift = (lighting.shadow_index % 4) * 8;
- uint mask_bucket = lighting.shadow_index / 4;
- uint mask = (lighting.shadow_mask[mask_bucket] >> mask_shift) & 0xFF;
- shadow = mask / 255.0;
-#endif // RAYTRACING_INLINE
}
else
-#endif // RAYTRACED_SHADOWS_ENABLED
{
float4 ShPos = mul(MatrixArray[light.GetMatrixIndex() + 0], float4(surface.P, 1));
ShPos.xyz /= ShPos.w;
@@ -408,6 +403,17 @@ inline void SpotLight(in ShaderEntity light, in Surface surface, inout Lighting
shadow *= shadowCascade(light, ShPos.xyz, ShTex.xy, 0);
}
}
+
+#ifdef SHADOW_MASK_ENABLED
+ [branch]
+ if (g_xFrame_Options & OPTION_BIT_SHADOW_MASK)
+ {
+ uint mask_shift = (lighting.shadow_index % 4) * 8;
+ uint mask_bucket = lighting.shadow_index / 4;
+ uint mask = (lighting.shadow_mask[mask_bucket] >> mask_shift) & 0xFF;
+ shadow *= mask / 255.0;
+ }
+#endif // SHADOW_MASK_ENABLED
}
[branch]
@@ -430,7 +436,7 @@ inline void SpotLight(in ShaderEntity light, in Surface surface, inout Lighting
}
}
-#ifdef RAYTRACED_SHADOWS_ENABLED
+#ifdef SHADOW_MASK_ENABLED
#ifndef RAYTRACING_INLINE
[branch]
if (light.IsCastingShadow())
@@ -444,7 +450,7 @@ inline void SpotLight(in ShaderEntity light, in Surface surface, inout Lighting
lighting.shadow_index++;
}
#endif // RAYTRACING_INLINE
-#endif // RAYTRACED_SHADOWS_ENABLED
+#endif // SHADOW_MASK_ENABLED
}
diff --git a/WickedEngine/objectHF.hlsli b/WickedEngine/objectHF.hlsli
index 25f5daa38..a3a209da7 100644
--- a/WickedEngine/objectHF.hlsli
+++ b/WickedEngine/objectHF.hlsli
@@ -1486,13 +1486,13 @@ float4 main(PixelInput input) : SV_TARGET
#endif
-#ifdef RAYTRACED_SHADOWS_ENABLED
+#ifdef SHADOW_MASK_ENABLED
[branch]
- if (g_xFrame_Options & OPTION_BIT_RAYTRACED_SHADOWS)
+ if (g_xFrame_Options & OPTION_BIT_SHADOW_MASK)
{
lighting.shadow_mask = texture_rtshadow[surface.pixel];
}
-#endif // RAYTRACED_SHADOWS_ENABLED
+#endif // SHADOW_MASK_ENABLED
#ifdef FORWARD
diff --git a/WickedEngine/objectPS.hlsl b/WickedEngine/objectPS.hlsl
index dfba3cac0..6f2281955 100644
--- a/WickedEngine/objectPS.hlsl
+++ b/WickedEngine/objectPS.hlsl
@@ -1,6 +1,6 @@
#define OBJECTSHADER_COMPILE_PS
#define OBJECTSHADER_LAYOUT_COMMON
-#define RAYTRACED_SHADOWS_ENABLED
+#define SHADOW_MASK_ENABLED
#define OUTPUT_GBUFFER
#define TILEDFORWARD
#define DISABLE_ALPHATEST
diff --git a/WickedEngine/objectPS_anisotropic.hlsl b/WickedEngine/objectPS_anisotropic.hlsl
index 156734cf1..906d97c95 100644
--- a/WickedEngine/objectPS_anisotropic.hlsl
+++ b/WickedEngine/objectPS_anisotropic.hlsl
@@ -1,6 +1,6 @@
#define OBJECTSHADER_COMPILE_PS
#define OBJECTSHADER_LAYOUT_COMMON
-#define RAYTRACED_SHADOWS_ENABLED
+#define SHADOW_MASK_ENABLED
#define OUTPUT_GBUFFER
#define TILEDFORWARD
#define DISABLE_ALPHATEST
diff --git a/WickedEngine/objectPS_cartoon.hlsl b/WickedEngine/objectPS_cartoon.hlsl
index dd2a0a4f2..b279aba35 100644
--- a/WickedEngine/objectPS_cartoon.hlsl
+++ b/WickedEngine/objectPS_cartoon.hlsl
@@ -1,6 +1,6 @@
#define OBJECTSHADER_COMPILE_PS
#define OBJECTSHADER_LAYOUT_COMMON
-#define RAYTRACED_SHADOWS_ENABLED
+#define SHADOW_MASK_ENABLED
#define OUTPUT_GBUFFER
#define TILEDFORWARD
#define DISABLE_ALPHATEST
diff --git a/WickedEngine/objectPS_planarreflection.hlsl b/WickedEngine/objectPS_planarreflection.hlsl
index 1b8900d38..1aa6959b6 100644
--- a/WickedEngine/objectPS_planarreflection.hlsl
+++ b/WickedEngine/objectPS_planarreflection.hlsl
@@ -1,6 +1,6 @@
#define OBJECTSHADER_COMPILE_PS
#define OBJECTSHADER_LAYOUT_COMMON
-#define RAYTRACED_SHADOWS_ENABLED
+#define SHADOW_MASK_ENABLED
#define OUTPUT_GBUFFER
#define TILEDFORWARD
#define DISABLE_ALPHATEST
diff --git a/WickedEngine/objectPS_pom.hlsl b/WickedEngine/objectPS_pom.hlsl
index 980aff956..2e2ebd8f8 100644
--- a/WickedEngine/objectPS_pom.hlsl
+++ b/WickedEngine/objectPS_pom.hlsl
@@ -1,6 +1,6 @@
#define OBJECTSHADER_COMPILE_PS
#define OBJECTSHADER_LAYOUT_COMMON
-#define RAYTRACED_SHADOWS_ENABLED
+#define SHADOW_MASK_ENABLED
#define OUTPUT_GBUFFER
#define TILEDFORWARD
#define DISABLE_ALPHATEST
diff --git a/WickedEngine/objectPS_terrain.hlsl b/WickedEngine/objectPS_terrain.hlsl
index 1ab639526..e0bc645ad 100644
--- a/WickedEngine/objectPS_terrain.hlsl
+++ b/WickedEngine/objectPS_terrain.hlsl
@@ -1,6 +1,6 @@
#define OBJECTSHADER_COMPILE_PS
#define OBJECTSHADER_LAYOUT_COMMON
-#define RAYTRACED_SHADOWS_ENABLED
+#define SHADOW_MASK_ENABLED
#define OUTPUT_GBUFFER
#define TILEDFORWARD
#define DISABLE_ALPHATEST
diff --git a/WickedEngine/rtshadowLIB.hlsl b/WickedEngine/rtshadowLIB.hlsl
index 13a641978..6009b392c 100644
--- a/WickedEngine/rtshadowLIB.hlsl
+++ b/WickedEngine/rtshadowLIB.hlsl
@@ -48,6 +48,10 @@ void RTShadow_Raygen()
uint4 shadow_mask = 0;
uint shadow_index = 0;
+ RayDesc ray;
+ ray.TMin = 0.01;
+ ray.Origin = surface.P + surface.N * 0.1;
+
[branch]
if (g_xFrame_LightArrayCount > 0)
{
@@ -88,10 +92,7 @@ void RTShadow_Raygen()
continue; // static lights will be skipped (they are used in lightmap baking)
}
- RayDesc ray;
- ray.TMin = 0.01;
ray.TMax = 0;
- ray.Origin = trace_bias_position(surface.P, surface.N);
float3 L;
diff --git a/WickedEngine/screenspaceshadowCS.hlsl b/WickedEngine/screenspaceshadowCS.hlsl
new file mode 100644
index 000000000..378633cc0
--- /dev/null
+++ b/WickedEngine/screenspaceshadowCS.hlsl
@@ -0,0 +1,228 @@
+#define BRDF_NDOTL_BIAS 0.1
+#include "globals.hlsli"
+#include "ShaderInterop_Postprocess.h"
+#include "raytracingHF.hlsli"
+
+TEXTURE2D(texture_normals, float3, TEXSLOT_ONDEMAND0);
+STRUCTUREDBUFFER(EntityTiles, uint, TEXSLOT_RENDERPATH_ENTITYTILES);
+
+static const uint MAX_RTSHADOWS = 16;
+RWTEXTURE2D(output, uint4, 0);
+
+[numthreads(POSTPROCESS_BLOCKSIZE, POSTPROCESS_BLOCKSIZE, 1)]
+void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint3 GTid : SV_GroupThreadID, uint groupIndex : SV_GroupIndex)
+{
+ const float2 uv = ((float2)DTid.xy + 0.5f) * xPPResolution_rcp;
+ const float depth = texture_depth.SampleLevel(sampler_point_clamp, uv, 1);
+ if (depth == 0.0f)
+ return;
+
+ const float3 P = reconstructPosition(uv, depth);
+ float3 N = normalize(texture_normals.SampleLevel(sampler_point_clamp, uv, 0) * 2 - 1);
+
+ Surface surface;
+ surface.init();
+ surface.pixel = DTid.xy;
+ surface.P = P + N * 0.05;
+ surface.N = N;
+
+ const uint2 tileIndex = uint2(floor(surface.pixel * 2 / TILED_CULLING_BLOCKSIZE));
+ const uint flatTileIndex = flatten2D(tileIndex, g_xFrame_EntityCullingTileCount.xy) * SHADER_ENTITY_TILE_BUCKET_COUNT;
+
+ uint shadow_mask[4] = {0,0,0,0};
+ uint shadow_index = 0;
+
+ const float3 rayOrigin = mul(g_xCamera_View, float4(surface.P, 1)).xyz;
+
+ const float range = xPPParams0.x;
+ const uint samplecount = xPPParams0.y;
+ const float thickness = 0.1;
+ const float stepsize = range / samplecount;
+ const float offset = abs(dither(DTid.xy + GetTemporalAASampleRotation()));
+
+ [branch]
+ if (g_xFrame_LightArrayCount > 0)
+ {
+ // Loop through light buckets in the tile:
+ const uint first_item = g_xFrame_LightArrayOffset;
+ const uint last_item = first_item + g_xFrame_LightArrayCount - 1;
+ const uint first_bucket = first_item / 32;
+ const uint last_bucket = min(last_item / 32, max(0, SHADER_ENTITY_TILE_BUCKET_COUNT - 1));
+ [loop]
+ for (uint bucket = first_bucket; bucket <= last_bucket && shadow_index < MAX_RTSHADOWS; ++bucket)
+ {
+ uint bucket_bits = EntityTiles[flatTileIndex + bucket];
+
+ // Bucket scalarizer - Siggraph 2017 - Improved Culling [Michal Drobot]:
+ bucket_bits = WaveReadLaneFirst(WaveActiveBitOr(bucket_bits));
+
+ [loop]
+ while (bucket_bits != 0 && shadow_index < MAX_RTSHADOWS)
+ {
+ // Retrieve global entity index from local bucket, then remove bit from local bucket:
+ const uint bucket_bit_index = firstbitlow(bucket_bits);
+ const uint entity_index = bucket * 32 + bucket_bit_index;
+ bucket_bits ^= 1 << bucket_bit_index;
+
+ // Check if it is a light and process:
+ [branch]
+ if (entity_index >= first_item && entity_index <= last_item)
+ {
+ ShaderEntity light = EntityArray[entity_index];
+
+ if (!light.IsCastingShadow())
+ {
+ continue;
+ }
+
+ if (light.GetFlags() & ENTITY_FLAG_LIGHT_STATIC)
+ {
+ continue; // static lights will be skipped (they are used in lightmap baking)
+ }
+
+ float3 L;
+ bool has_shadow = false;
+
+ switch (light.GetType())
+ {
+ default:
+ case ENTITY_TYPE_DIRECTIONALLIGHT:
+ {
+ L = normalize(light.GetDirection());
+
+ SurfaceToLight surfaceToLight;
+ surfaceToLight.create(surface, L);
+
+ [branch]
+ if (any(surfaceToLight.NdotL))
+ {
+ [branch]
+ if (light.IsCastingShadow())
+ {
+ has_shadow = true;
+ }
+ }
+ }
+ break;
+ case ENTITY_TYPE_POINTLIGHT:
+ {
+ L = light.position - surface.P;
+ const float dist2 = dot(L, L);
+ const float range2 = light.GetRange() * light.GetRange();
+
+ [branch]
+ if (dist2 < range2)
+ {
+ const float3 Lunnormalized = L;
+ const float dist = sqrt(dist2);
+ L /= dist;
+
+ SurfaceToLight surfaceToLight;
+ surfaceToLight.create(surface, L);
+
+ [branch]
+ if (any(surfaceToLight.NdotL))
+ {
+ has_shadow = true;
+ }
+ }
+ }
+ break;
+ case ENTITY_TYPE_SPOTLIGHT:
+ {
+ L = light.position - surface.P;
+ const float dist2 = dot(L, L);
+ const float range2 = light.GetRange() * light.GetRange();
+
+ [branch]
+ if (dist2 < range2)
+ {
+ const float dist = sqrt(dist2);
+ L /= dist;
+
+ SurfaceToLight surfaceToLight;
+ surfaceToLight.create(surface, L);
+
+ [branch]
+ if (any(surfaceToLight.NdotL_sss))
+ {
+ const float SpotFactor = dot(L, light.GetDirection());
+ const float spotCutOff = light.GetConeAngleCos();
+
+ [branch]
+ if (SpotFactor > spotCutOff)
+ {
+ has_shadow = true;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ [branch]
+ if (has_shadow)
+ {
+ const float3 rayDirection = normalize(mul((float3x3)g_xCamera_View, L));
+ float3 rayPos = rayOrigin + rayDirection * stepsize * offset;
+
+ float occlusion = 0;
+
+ [loop]
+ for (uint i = 0; i < samplecount; ++i)
+ {
+ rayPos += rayDirection * stepsize;
+
+ float4 proj = mul(g_xCamera_Proj, float4(rayPos, 1));
+ proj.xyz /= proj.w;
+ proj.xy = proj.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f);
+
+ [branch]
+ if (is_saturated(proj.xy))
+ {
+ const float ray_depth_real = proj.w;
+ const float ray_depth_sample = texture_lineardepth.SampleLevel(sampler_point_clamp, proj.xy, 0) * g_xCamera_ZFarP;
+ const float ray_depth_delta = ray_depth_real - ray_depth_sample;
+ if (ray_depth_delta > 0 && ray_depth_delta < thickness)
+ {
+ occlusion = 1;
+
+ // screen edge fade:
+ float2 fade = max(12 * abs(uv - 0.5) - 5, 0);
+ occlusion *= saturate(1 - dot(fade, fade));
+
+ break;
+ }
+ }
+ }
+ float shadow = 1 - occlusion;
+
+ uint mask = uint(saturate(shadow) * 255); // 8 bits
+ uint mask_shift = (shadow_index % 4) * 8;
+ uint mask_bucket = shadow_index / 4;
+ shadow_mask[mask_bucket] |= mask << mask_shift;
+ }
+
+ // (**) cannot detect exactly same contribution as is pixel shaders!
+ // So we always increment it for shadowed light, even if the
+ // shadow contribution is not traced
+ //
+ // This is because in the pixel shader, we will detect the shadow
+ // contribution more precisely due to more precise surface normals
+ shadow_index++;
+
+ }
+ else if (entity_index > last_item)
+ {
+ // force exit:
+ bucket = SHADER_ENTITY_TILE_BUCKET_COUNT;
+ break;
+ }
+
+ }
+
+ }
+ }
+
+ output[DTid.xy] = uint4(shadow_mask[0], shadow_mask[1], shadow_mask[2], shadow_mask[3]);
+}
diff --git a/WickedEngine/wiEnums.h b/WickedEngine/wiEnums.h
index 3aaf90c92..9282c7582 100644
--- a/WickedEngine/wiEnums.h
+++ b/WickedEngine/wiEnums.h
@@ -377,6 +377,7 @@ enum SHADERTYPE
CSTYPE_POSTPROCESS_UPSAMPLE_BILATERAL_UINT4,
CSTYPE_POSTPROCESS_DOWNSAMPLE4X,
CSTYPE_POSTPROCESS_NORMALSFROMDEPTH,
+ CSTYPE_POSTPROCESS_SCREENSPACESHADOW,
diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp
index b408c2c76..9674ee417 100644
--- a/WickedEngine/wiRenderer.cpp
+++ b/WickedEngine/wiRenderer.cpp
@@ -97,6 +97,7 @@ bool raytracedShadows = false;
bool tessellationEnabled = true;
bool disableAlbedoMaps = false;
uint32_t raytracedShadowsSampleCount = 1;
+bool SCREENSPACESHADOWS = false;
struct VoxelizedSceneData
@@ -1236,6 +1237,7 @@ void LoadShaders()
wiJobSystem::Execute(ctx, [](wiJobArgs args) { LoadShader(CS, shaders[CSTYPE_POSTPROCESS_UPSAMPLE_BILATERAL_UINT4], "upsample_bilateral_uint4CS.cso"); });
wiJobSystem::Execute(ctx, [](wiJobArgs args) { LoadShader(CS, shaders[CSTYPE_POSTPROCESS_DOWNSAMPLE4X], "downsample4xCS.cso"); });
wiJobSystem::Execute(ctx, [](wiJobArgs args) { LoadShader(CS, shaders[CSTYPE_POSTPROCESS_NORMALSFROMDEPTH], "normalsfromdepthCS.cso"); });
+ wiJobSystem::Execute(ctx, [](wiJobArgs args) { LoadShader(CS, shaders[CSTYPE_POSTPROCESS_SCREENSPACESHADOW], "screenspaceshadowCS.cso"); });
wiJobSystem::Execute(ctx, [](wiJobArgs args) { LoadShader(HS, shaders[HSTYPE_OBJECT], "objectHS.cso"); });
@@ -3656,11 +3658,16 @@ void UpdatePerFrameData(
if (device->CheckCapability(GRAPHICSDEVICE_CAPABILITY_RAYTRACING) && GetRaytracedShadowsEnabled())
{
frameCB.g_xFrame_Options |= OPTION_BIT_RAYTRACED_SHADOWS;
+ frameCB.g_xFrame_Options |= OPTION_BIT_SHADOW_MASK;
}
- if (disableAlbedoMaps)
+ if (IsDisableAlbedoMaps())
{
frameCB.g_xFrame_Options |= OPTION_BIT_DISABLE_ALBEDO_MAPS;
}
+ if (GetScreenSpaceShadowsEnabled())
+ {
+ frameCB.g_xFrame_Options |= OPTION_BIT_SHADOW_MASK;
+ }
}
void UpdateRenderData(
const Visibility& vis,
@@ -10472,6 +10479,93 @@ void Postprocess_RTShadow(
wiProfiler::EndRange(prof_range);
device->EventEnd(cmd);
}
+void Postprocess_ScreenSpaceShadow(
+ const Texture& depthbuffer,
+ const Texture& lineardepth,
+ const GPUBuffer& entityTiles_Opaque,
+ const Texture& output,
+ CommandList cmd,
+ float range,
+ uint32_t samplecount
+)
+{
+ device->EventBegin("Postprocess_ScreenSpaceShadow", cmd);
+ auto prof_range = wiProfiler::BeginRangeGPU("ScreenSpaceShadow", cmd);
+
+ static TextureDesc saved_desc;
+ static Texture temp;
+ static Texture temporal[2];
+ static Texture normals;
+
+ const TextureDesc& lineardepth_desc = lineardepth.GetDesc();
+ if (saved_desc.Width != lineardepth_desc.Width || saved_desc.Height != lineardepth_desc.Height)
+ {
+ saved_desc = lineardepth_desc; // <- this must already have SRV and UAV request flags set up!
+ saved_desc.MipLevels = 1;
+
+ TextureDesc desc = saved_desc;
+ desc.Width = (desc.Width + 1) / 2;
+ desc.Height = (desc.Height + 1) / 2;
+ desc.Format = FORMAT_R32G32B32A32_UINT;
+ device->CreateTexture(&desc, nullptr, &temp);
+ device->SetName(&temp, "rtshadow_temp");
+
+ device->CreateTexture(&desc, nullptr, &temporal[0]);
+ device->SetName(&temporal[0], "rtshadow_temporal[0]");
+ device->CreateTexture(&desc, nullptr, &temporal[1]);
+ device->SetName(&temporal[1], "rtshadow_temporal[1]");
+
+ desc.Format = FORMAT_R11G11B10_FLOAT;
+ device->CreateTexture(&desc, nullptr, &normals);
+ device->SetName(&normals, "rtshadow_normals");
+ }
+
+ Postprocess_NormalsFromDepth(depthbuffer, normals, cmd);
+
+ const TextureDesc& desc = temp.GetDesc();
+
+ PostProcessCB cb;
+ cb.xPPResolution.x = desc.Width;
+ cb.xPPResolution.y = desc.Height;
+ cb.xPPResolution_rcp.x = 1.0f / cb.xPPResolution.x;
+ cb.xPPResolution_rcp.y = 1.0f / cb.xPPResolution.y;
+ cb.xPPParams0.x = range;
+ cb.xPPParams0.y = (float)samplecount;
+ device->UpdateBuffer(&constantBuffers[CBTYPE_POSTPROCESS], &cb, cmd);
+ device->BindConstantBuffer(CS, &constantBuffers[CBTYPE_POSTPROCESS], CB_GETBINDSLOT(PostProcessCB), cmd);
+
+ device->BindComputeShader(&shaders[CSTYPE_POSTPROCESS_SCREENSPACESHADOW], cmd);
+
+ device->BindResource(CS, &depthbuffer, TEXSLOT_DEPTH, cmd);
+ device->BindResource(CS, &lineardepth, TEXSLOT_LINEARDEPTH, cmd);
+
+ device->BindResource(CS, &normals, TEXSLOT_ONDEMAND0, cmd);
+ device->BindResource(CS, &entityTiles_Opaque, TEXSLOT_RENDERPATH_ENTITYTILES, cmd);
+
+ const GPUResource* uavs[] = {
+ &temp,
+ };
+ device->BindUAVs(CS, uavs, 0, arraysize(uavs), cmd);
+
+ device->Dispatch(
+ (desc.Width + POSTPROCESS_BLOCKSIZE - 1) / POSTPROCESS_BLOCKSIZE,
+ (desc.Height + POSTPROCESS_BLOCKSIZE - 1) / POSTPROCESS_BLOCKSIZE,
+ 1,
+ cmd
+ );
+
+ GPUBarrier barriers[] = {
+ GPUBarrier::Memory(),
+ };
+ device->Barrier(barriers, arraysize(barriers), cmd);
+
+ device->UnbindUAVs(0, arraysize(uavs), cmd);
+
+ Postprocess_Upsample_Bilateral(temp, lineardepth, output, cmd);
+
+ wiProfiler::EndRange(prof_range);
+ device->EventEnd(cmd);
+}
void Postprocess_LightShafts(
const Texture& input,
const Texture& output,
@@ -12305,5 +12399,13 @@ uint32_t GetRaytracedShadowsSampleCount()
{
return raytracedShadowsSampleCount;
}
+void SetScreenSpaceShadowsEnabled(bool value)
+{
+ SCREENSPACESHADOWS = value;
+}
+bool GetScreenSpaceShadowsEnabled()
+{
+ return SCREENSPACESHADOWS;
+}
}
diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h
index 6e7083962..266586db1 100644
--- a/WickedEngine/wiRenderer.h
+++ b/WickedEngine/wiRenderer.h
@@ -353,6 +353,15 @@ namespace wiRenderer
const wiGraphics::Texture& output,
wiGraphics::CommandList cmd
);
+ void Postprocess_ScreenSpaceShadow(
+ const wiGraphics::Texture& depthbuffer,
+ const wiGraphics::Texture& lineardepth,
+ const wiGraphics::GPUBuffer& entityTiles_Opaque,
+ const wiGraphics::Texture& output,
+ wiGraphics::CommandList cmd,
+ float range = 1,
+ uint32_t samplecount = 16
+ );
void Postprocess_LightShafts(
const wiGraphics::Texture& input,
const wiGraphics::Texture& output,
@@ -618,6 +627,8 @@ namespace wiRenderer
bool IsDisableAlbedoMaps();
void SetRaytracedShadowsSampleCount(uint32_t value);
uint32_t GetRaytracedShadowsSampleCount();
+ void SetScreenSpaceShadowsEnabled(bool value);
+ bool GetScreenSpaceShadowsEnabled();
const wiGraphics::Texture* GetGlobalLightmap();
diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp
index c5280090e..a4e7e39cf 100644
--- a/WickedEngine/wiVersion.cpp
+++ b/WickedEngine/wiVersion.cpp
@@ -9,7 +9,7 @@ namespace wiVersion
// minor features, major updates, breaking compatibility changes
const int minor = 52;
// minor bug fixes, alterations, refactors, updates
- const int revision = 13;
+ const int revision = 14;
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);
diff --git a/features.txt b/features.txt
index 0bec5f83d..0e3382b4f 100644
--- a/features.txt
+++ b/features.txt
@@ -87,3 +87,4 @@ Springs
Terrain Rendering (material blending)
Variable Rate Shading
Real time ray tracing: ambient occlusion, shadows, reflections (DXR and Vulkan raytracing)
+Screen Space Contact Shadows