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