diff --git a/WickedEngine/shaders/globals.hlsli b/WickedEngine/shaders/globals.hlsli index 13a4c928c..98caa6c70 100644 --- a/WickedEngine/shaders/globals.hlsli +++ b/WickedEngine/shaders/globals.hlsli @@ -897,7 +897,7 @@ inline float2 clipspace_to_uv(in float2 clipspace) } inline float3 clipspace_to_uv(in float3 clipspace) { - return clipspace * float3(0.5, -0.5, 0.5) + 0.5; + return clipspace * float3(0.5, -0.5, 1) + float3(0.5, 0.5, 0); } inline half2 clipspace_to_uv(in half2 clipspace) { @@ -905,7 +905,7 @@ inline half2 clipspace_to_uv(in half2 clipspace) } inline half3 clipspace_to_uv(in half3 clipspace) { - return clipspace * half3(0.5, -0.5, 0.5) + 0.5; + return clipspace * half3(0.5, -0.5, 1) + half3(0.5, 0.5, 0); } inline half3 GetSunColor() { return unpack_half3(GetWeather().sun_color); } // sun color with intensity applied diff --git a/WickedEngine/shaders/lightingHF.hlsli b/WickedEngine/shaders/lightingHF.hlsli index 6c17d5414..2d1a79fd0 100644 --- a/WickedEngine/shaders/lightingHF.hlsli +++ b/WickedEngine/shaders/lightingHF.hlsli @@ -89,8 +89,9 @@ inline void light_directional(in ShaderEntity light, in Surface surface, inout L [branch] if (is_saturated(shadow_uv)) { - const half2 cascade_edgefactor = saturate(saturate(abs(shadow_pos.xy)) - 0.8) * 5.0; // fade will be on edge and inwards 10% - const half cascade_fade = max(cascade_edgefactor.x, cascade_edgefactor.y); + const half3 shadow_box = half3(shadow_pos.xy, shadow_pos.z * 2 - 1); + const half3 cascade_edgefactor = saturate(saturate(abs(shadow_box)) - 0.8) * 5.0; // fade will be on edge and inwards 10% + const half cascade_fade = max3(cascade_edgefactor); // If we are on cascade edge threshold and not the last cascade, then fallback to a larger cascade: [branch] diff --git a/WickedEngine/shaders/shadowHF.hlsli b/WickedEngine/shaders/shadowHF.hlsli index 64b82be2b..5388907d4 100644 --- a/WickedEngine/shaders/shadowHF.hlsli +++ b/WickedEngine/shaders/shadowHF.hlsli @@ -2,17 +2,16 @@ #define WI_SHADOW_HF #include "globals.hlsli" -static const float exponential_shadow_bias = 40; +static const float exponential_shadow_bias = 80; -inline half3 sample_shadow(float2 uv, float cmp, float4 uv_clamping, half2 radius, float shadow_power = 1) +inline half3 sample_shadow(float2 uv, float cmp) { Texture2D texture_shadowatlas = bindless_textures_float[descriptor_index(GetFrame().texture_shadowatlas_index)]; - Texture2D texture_shadowatlas_transparent = bindless_textures_half4[descriptor_index(GetFrame().texture_shadowatlas_transparent_index)]; - float shadowMapValue = texture_shadowatlas.SampleLevel(sampler_linear_clamp, uv, 0); - half3 shadow = pow(saturate(shadowMapValue * exp(-exponential_shadow_bias * cmp)), shadow_power); + half3 shadow = saturate(shadowMapValue * exp(-exponential_shadow_bias * cmp)); #ifndef DISABLE_TRANSPARENT_SHADOWMAP + Texture2D texture_shadowatlas_transparent = bindless_textures_half4[descriptor_index(GetFrame().texture_shadowatlas_transparent_index)]; half4 transparent_shadow = texture_shadowatlas_transparent.SampleLevel(sampler_linear_clamp, uv, 0); #ifdef TRANSPARENT_SHADOWMAP_SECONDARY_DEPTH_CHECK if (transparent_shadow.a > cmp) @@ -26,19 +25,20 @@ inline half3 sample_shadow(float2 uv, float cmp, float4 uv_clamping, half2 radiu } // 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) +inline void shadow_border_clamp(in ShaderEntity light, in float slice, inout float2 uv) { 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); + uv = clamp(uv, topleft, bottomright); } inline half3 shadow_2D(in ShaderEntity light, in float z, in float2 shadow_uv, in uint cascade, in float shadow_power = 1) { shadow_uv.x += cascade; shadow_uv = mad(shadow_uv, light.shadowAtlasMulAdd.xy, light.shadowAtlasMulAdd.zw); - return sample_shadow(shadow_uv, z, shadow_border_clamp(light, cascade), light.GetType() == ENTITY_TYPE_RECTLIGHT ? (half2(light.GetRadius(), light.GetLength()) * 0.05) : light.GetRadius(), shadow_power); + shadow_border_clamp(light, cascade, shadow_uv); + return sample_shadow(shadow_uv, z); } inline half3 shadow_cube(in ShaderEntity light, in float3 Lunnormalized, in float shadow_power = 1) @@ -47,7 +47,8 @@ inline half3 shadow_cube(in ShaderEntity light, in float3 Lunnormalized, in floa 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, length(Lunnormalized) / light.GetRange(), shadow_border_clamp(light, uv_slice.z), light.GetRadius(), shadow_power); + shadow_border_clamp(light, uv_slice.z, shadow_uv); + return sample_shadow(shadow_uv, length(Lunnormalized) / light.GetRange()); } inline half shadow_2D_volumetricclouds(float3 P) diff --git a/WickedEngine/shaders/shadow_filterCS.hlsl b/WickedEngine/shaders/shadow_filterCS.hlsl index e55581269..ba3d3434c 100644 --- a/WickedEngine/shaders/shadow_filterCS.hlsl +++ b/WickedEngine/shaders/shadow_filterCS.hlsl @@ -4,8 +4,10 @@ ConstantBuffer filter : register(b2); Texture2D shadowAtlas : register(t0); +Texture2D shadowAtlas_transparent : register(t1); RWTexture2D shadowAtlas_filtered : register(u0); +RWTexture2D shadowAtlas_transparent_filtered : register(u1); static const float kGoldenAngle = 2.4; @@ -22,6 +24,7 @@ void main(uint3 DTid : SV_DispatchThreadID) const float2 bottomright = (filter.rect.xy + filter.rect.zw - border) * filter.atlas_resolution_rcp; float filtered = 0; + float transparent_filtered = 0; const float2 spread_offset = filter.atlas_resolution_rcp * (2 + filter.spread * 8); const uint soft_shadow_sample_count = (uint)lerp(8.0, 128.0, saturate(length(filter.spread))); @@ -55,11 +58,14 @@ void main(uint3 DTid : SV_DispatchThreadID) zzzz = saturate(zzzz); zzzz = exp(exponential_shadow_bias * zzzz); filtered += bilinear(zzzz, frac(sample_uv * filter.atlas_resolution)); + transparent_filtered += shadowAtlas_transparent.SampleLevel(sampler_linear_clamp, sample_uv, 0); } filtered *= soft_shadow_sample_count_rcp; + transparent_filtered *= soft_shadow_sample_count_rcp; if(all(DTid.xy < filter.rect.zw)) { shadowAtlas_filtered[pixel] = filtered; + shadowAtlas_transparent_filtered[pixel] = transparent_filtered; } } diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index 633e37d5a..d7bdf9ca2 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -150,6 +150,7 @@ bool SHADOW_LOD_OVERRIDE = true; Texture shadowMapAtlas; Texture shadowMapAtlas_Transparent; Texture shadowMapAtlas_Filtered; +Texture shadowMapAtlas_Transparent_Filtered; int max_shadow_resolution_2D = 1024; int max_shadow_resolution_cube = 256; @@ -2902,16 +2903,17 @@ inline void CreateDirLightShadowCams(const LightComponent& light, CameraComponen XMStoreFloat3(&_min, vMin); XMStoreFloat3(&_max, vMax); - // Extrude bounds to avoid early shadow clipping: + 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); + + // 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); + const XMMATRIX lightProjectionExtended = XMMatrixOrthographicOffCenterLH(_min.x, _max.x, _min.y, _max.y, _max.z, _min.z); // notice reversed Z! + shcams[cascade].frustum.Create(XMMatrixMultiply(lightView, lightProjectionExtended)); } } @@ -3934,11 +3936,6 @@ void UpdateVisibility(Visibility& vis) device->CreateTexture(&desc, nullptr, &shadowMapAtlas); device->SetName(&shadowMapAtlas, "shadowMapAtlas"); - desc.format = Format::R32_FLOAT; - 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; @@ -3949,6 +3946,14 @@ void UpdateVisibility(Visibility& vis) device->CreateTexture(&desc, nullptr, &shadowMapAtlas_Transparent); device->SetName(&shadowMapAtlas_Transparent, "shadowMapAtlas_Transparent"); + desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADER_RESOURCE; + desc.format = Format::R32_FLOAT; + device->CreateTexture(&desc, nullptr, &shadowMapAtlas_Filtered); + device->SetName(&shadowMapAtlas_Filtered, "shadowMapAtlas_Filtered"); + desc.format = format_rendertarget_shadowmap; + device->CreateTexture(&desc, nullptr, &shadowMapAtlas_Transparent_Filtered); + device->SetName(&shadowMapAtlas_Transparent_Filtered, "shadowMapAtlas_Transparent_Filtered"); + } break; @@ -4241,7 +4246,7 @@ void UpdatePerFrameData( // Note: shadow maps always assumed to be valid to avoid shader branching logic const Texture& shadowMap = shadowMapAtlas_Filtered.IsValid() ? shadowMapAtlas_Filtered : *wi::texturehelper::getBlack(); - const Texture& shadowMapTransparent = shadowMapAtlas_Transparent.IsValid() ? shadowMapAtlas_Transparent : *wi::texturehelper::getWhite(); + const Texture& shadowMapTransparent = shadowMapAtlas_Transparent_Filtered.IsValid() ? shadowMapAtlas_Transparent_Filtered : *wi::texturehelper::getWhite(); frameCB.texture_shadowatlas_index = device->GetDescriptorIndex(&shadowMap, SubresourceType::SRV); frameCB.texture_shadowatlas_transparent_index = device->GetDescriptorIndex(&shadowMapTransparent, SubresourceType::SRV); frameCB.shadow_atlas_resolution.x = shadowMap.desc.width; @@ -7006,11 +7011,16 @@ void DrawShadowmaps( device->RenderPassEnd(cmd); device->EventBegin("Shadow Filtering", cmd); device->Barrier(GPUBarrier::Image(&shadowMapAtlas_Filtered, ResourceState::UNDEFINED, ResourceState::UNORDERED_ACCESS), cmd); + device->Barrier(GPUBarrier::Image(&shadowMapAtlas_Transparent_Filtered, ResourceState::UNDEFINED, ResourceState::UNORDERED_ACCESS), cmd); device->ClearUAV(&shadowMapAtlas_Filtered, 0, cmd); + device->ClearUAV(&shadowMapAtlas_Transparent_Filtered, 0, cmd); device->Barrier(GPUBarrier::Memory(&shadowMapAtlas_Filtered), cmd); + device->Barrier(GPUBarrier::Memory(&shadowMapAtlas_Transparent_Filtered), cmd); device->BindComputeShader(&shaders[CSTYPE_SHADOW_FILTER], cmd); device->BindResource(&shadowMapAtlas, 0, cmd); + device->BindResource(&shadowMapAtlas_Transparent, 1, cmd); device->BindUAV(&shadowMapAtlas_Filtered, 0, cmd); + device->BindUAV(&shadowMapAtlas_Transparent_Filtered, 1, cmd); ShadowFilterData filter = {}; filter.atlas_resolution.x = float(shadowMapAtlas.desc.width); filter.atlas_resolution.y = float(shadowMapAtlas.desc.height); @@ -7164,6 +7174,7 @@ void DrawShadowmaps( device->Dispatch((filter.rect.z + 7u) / 8u, (filter.rect.w + 7u) / 8u, 1, cmd); } device->Barrier(GPUBarrier::Image(&shadowMapAtlas_Filtered, ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), cmd); + device->Barrier(GPUBarrier::Image(&shadowMapAtlas_Transparent_Filtered, ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), cmd); device->EventEnd(cmd); wi::profiler::EndRange(range_gpu); diff --git a/WickedEngine/wiTerrain.cpp b/WickedEngine/wiTerrain.cpp index 3ceee4bb0..eb4a94da8 100644 --- a/WickedEngine/wiTerrain.cpp +++ b/WickedEngine/wiTerrain.cpp @@ -742,6 +742,7 @@ namespace wi::terrain if (chunk_object != nullptr) { chunk_object->SetWetmapEnabled(scene->IsWetmapProcessingRequired()); + chunk_object->SetCastShadow(true); } // chunk removal: