diff --git a/features/Skylighting/Shaders/Skylighting/Skylighting.hlsli b/features/Skylighting/Shaders/Skylighting/Skylighting.hlsli index fbbe1fe448..3c9af9b90e 100644 --- a/features/Skylighting/Shaders/Skylighting/Skylighting.hlsli +++ b/features/Skylighting/Shaders/Skylighting/Skylighting.hlsli @@ -14,6 +14,10 @@ namespace Skylighting Texture3D SkylightingProbeArray : register(t50); #endif +#if defined(SKYLIGHTING_SHADOW_VIS) + Texture3D ShadowVisibilityProbeArray : register(t53); +#endif + const static sh2 UNIT_SH = float4(sqrt(4.0 * Math::PI), 0, 0, 0); const static uint3 ARRAY_DIM = uint3(256, 256, 128); @@ -75,10 +79,18 @@ namespace Skylighting #endif #if defined(PSHADER) || defined(SKYLIGHTING_PROBE_REGISTER) - sh2 Sample(float3 positionMS, float3 normalWS) + sh2 Sample(float3 positionMS, float3 normalWS +#if defined(SKYLIGHTING_SHADOW_VIS) + , out float shadowVisibility +#endif + ) { sh2 scaledUnitSH = UNIT_SH / 1e-10; +#if defined(SKYLIGHTING_SHADOW_VIS) + shadowVisibility = 1.0; +#endif + if (SharedData::InInterior) return scaledUnitSH; @@ -94,55 +106,63 @@ namespace Skylighting int3 cell000 = floor(cellVxCoord - 0.5); float3 trilinearPos = cellVxCoord - 0.5 - cell000; - sh2 sum = 0; - float wsum = 0; + sh2 shSum = 0; + float shWsum = 0; +#if defined(SKYLIGHTING_SHADOW_VIS) + float shadowSum = 0; + float shadowWsum = 0; +#endif + for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) for (int k = 0; k < 2; k++) { - int3 offset = int3(i, j, k); - int3 cellID = cell000 + offset; + int3 cellOffset = int3(i, j, k); + int3 cellID = cell000 + cellOffset; if (any(cellID < 0) || any((uint3)cellID >= ARRAY_DIM)) continue; - float3 cellCentreMS = cellID + 0.5 - ARRAY_DIM / 2; - cellCentreMS = cellCentreMS * CELL_SIZE; + float3 cellCentreMS = (cellID + 0.5 - ARRAY_DIM / 2) * CELL_SIZE; + + float3 trilinearWeights = 1 - abs(cellOffset - trilinearPos); + float triW = trilinearWeights.x * trilinearWeights.y * trilinearWeights.z; + + uint3 cellTexID = (cellID + SharedData::skylightingSettings.ArrayOrigin.xyz) % ARRAY_DIM; // https://handmade.network/p/75/monter/blog/p/7288-engine_work__global_illumination_with_irradiance_probes // basic tangent checks float tangentWeight = dot(normalize(cellCentreMS - positionMSAdjusted), normalWS) * 0.5 + 0.5; + float shW = triW * tangentWeight; + shSum = SphericalHarmonics::Add(shSum, SphericalHarmonics::Scale(SkylightingProbeArray[cellTexID], shW)); + shWsum += shW; - float3 trilinearWeights = 1 - abs(offset - trilinearPos); - float w = trilinearWeights.x * trilinearWeights.y * trilinearWeights.z * tangentWeight; - - uint3 cellTexID = (cellID + SharedData::skylightingSettings.ArrayOrigin.xyz) % ARRAY_DIM; - sh2 probe = SphericalHarmonics::Scale(SkylightingProbeArray[cellTexID], w); - - sum = SphericalHarmonics::Add(sum, probe); - wsum += w; +#if defined(SKYLIGHTING_SHADOW_VIS) + shadowSum += ShadowVisibilityProbeArray[cellTexID] * triW; + shadowWsum += triW; +#endif } - return SphericalHarmonics::Scale(sum, rcp(wsum + EPSILON_WEIGHT_SUM)); +#if defined(SKYLIGHTING_SHADOW_VIS) + float fadeOut = GetFadeOutFactor(positionMS); + shadowVisibility = lerp(1.0, shadowSum / max(shadowWsum, EPSILON_WEIGHT_SUM), fadeOut); +#endif + + return SphericalHarmonics::Scale(shSum, rcp(shWsum + EPSILON_WEIGHT_SUM)); } - // Compute skylighting diffuse for a receiver biased to face upward (grass/foliage). - // The result is pre-divided by vertexAO so that a subsequent multiply by vertexAO - // yields min(skylightingDiffuse, vertexAO). Pass vertexAO = 1 to skip this compensation. - float GetVertexSkylightingDiffuse(float3 positionMS, float3 normalWS, float vertexAO) + float GetSkylightingDiffuse(sh2 skylightingSH, float3 positionMS, float3 evalNormal, float vertexAO = 1.0) { if (SharedData::InInterior) return 1.0; + float3 candidateNormal = float3(evalNormal.xy, max(0.0, evalNormal.z)); + float3 biasedNormal = dot(candidateNormal, candidateNormal) > 1e-6 + ? normalize(candidateNormal) + : float3(0, 0, 1); float fadeOutFactor = GetFadeOutFactor(positionMS); - - float3 biasedNormal = normalWS; - biasedNormal.z = max(0.0, biasedNormal.z); - biasedNormal = normalize(biasedNormal); - - sh2 skylightingSH = Sample(positionMS, normalWS); float skylightingDiffuse = EvaluateDiffuse(skylightingSH, biasedNormal, fadeOutFactor); - return saturate(skylightingDiffuse / max(vertexAO, 1e-5)); + return saturate(skylightingDiffuse / max(vertexAO, EPSILON_DIVISION)); } sh2 SampleNoBias(float3 positionMS) @@ -174,16 +194,11 @@ namespace Skylighting if (any(cellID < 0) || any((uint3)cellID >= ARRAY_DIM)) continue; - float3 cellCentreMS = cellID + 0.5 - ARRAY_DIM / 2; - cellCentreMS = cellCentreMS * CELL_SIZE; - float3 trilinearWeights = 1 - abs(offset - trilinearPos); float w = trilinearWeights.x * trilinearWeights.y * trilinearWeights.z; uint3 cellTexID = (cellID + SharedData::skylightingSettings.ArrayOrigin.xyz) % ARRAY_DIM; - sh2 probe = SphericalHarmonics::Scale(SkylightingProbeArray[cellTexID], w); - - sum = SphericalHarmonics::Add(sum, probe); + sum = SphericalHarmonics::Add(sum, SphericalHarmonics::Scale(SkylightingProbeArray[cellTexID], w)); wsum += w; } diff --git a/features/Skylighting/Shaders/Skylighting/UpdateProbesCS.hlsl b/features/Skylighting/Shaders/Skylighting/UpdateProbesCS.hlsl index c2906f4619..9a07594db6 100644 --- a/features/Skylighting/Shaders/Skylighting/UpdateProbesCS.hlsl +++ b/features/Skylighting/Shaders/Skylighting/UpdateProbesCS.hlsl @@ -1,13 +1,61 @@ +#include "Common/FrameBuffer.hlsli" #include "Common/Math.hlsli" #include "Skylighting/Skylighting.hlsli" Texture2D srcOcclusionDepth : register(t0); +Texture2DArray ShadowCascadeMap : register(t1); + +struct DirectionalShadowLightData +{ + column_major float4x4 ShadowProj[2]; + column_major float4x4 InvShadowProj[2]; + float2 EndSplitDistances; + float2 StartSplitDistances; +}; +StructuredBuffer DirectionalShadowLights : register(t2); RWTexture3D outProbeArray : register(u0); RWTexture3D outAccumFramesArray : register(u1); +RWTexture3D outShadowBitmask : register(u2); +RWTexture3D outShadowVisibility : register(u3); SamplerComparisonState comparisonSampler : register(s0); +static const float3 noise3D[32] = { + float3(0.247, -0.583, 0.891), + float3(-0.672, 0.315, -0.428), + float3(0.934, 0.762, -0.153), + float3(-0.391, -0.847, 0.526), + float3(0.618, 0.094, 0.739), + float3(-0.825, -0.271, -0.683), + float3(0.152, 0.968, 0.347), + float3(0.503, -0.714, -0.592), + float3(-0.436, 0.629, 0.814), + float3(0.887, -0.198, 0.461), + float3(-0.759, 0.852, -0.305), + float3(0.321, -0.476, -0.921), + float3(-0.094, 0.543, -0.768), + float3(0.776, 0.418, 0.632), + float3(-0.538, -0.695, 0.279), + float3(0.649, -0.921, 0.186), + float3(-0.913, 0.127, 0.574), + float3(0.285, 0.806, -0.447), + float3(0.471, -0.352, 0.698), + float3(-0.627, -0.194, -0.856), + float3(0.834, 0.591, -0.712), + float3(-0.173, -0.968, -0.421), + float3(0.562, 0.239, -0.785), + float3(-0.745, 0.487, 0.316), + float3(0.108, -0.631, 0.894), + float3(0.926, -0.845, -0.267), + float3(-0.384, 0.712, -0.539), + float3(0.697, 0.163, 0.825), + float3(-0.851, -0.429, 0.641), + float3(0.214, 0.934, 0.372), + float3(0.578, -0.762, -0.614), + float3(-0.469, 0.381, 0.947) +}; + [numthreads(8, 8, 1)] void main(uint3 dtid : SV_DispatchThreadID) { const float fadeInThreshold = 15; const static sh2 unitSH = Skylighting::UNIT_SH; @@ -39,8 +87,51 @@ SamplerComparisonState comparisonSampler : register(s0); outProbeArray[dtid] = occlusionSH; outAccumFramesArray[dtid] = accumFrames; + + // Shadow cascade sampling with bitmask accumulation + { + float shadowSample = 1.0; + DirectionalShadowLightData shadowData = DirectionalShadowLights[0]; + + uint shadowFrames = max(accumFrames, 1u); + uint bitIndex = (shadowFrames - 1) % 32; + float3 jitteredMS = cellCentreMS + noise3D[bitIndex] * Skylighting::CELL_SIZE; + + float ndcDepth = FrameBuffer::GetShadowDepth(jitteredMS); + float linearDepth = SharedData::GetScreenDepth(ndcDepth); + + if (linearDepth > 0 && linearDepth < shadowData.EndSplitDistances.y) { + float3 positionWS = jitteredMS + FrameBuffer::CameraPosAdjust.xyz; + + uint cascadeIndex = (linearDepth > shadowData.EndSplitDistances.x) ? 1u : 0u; + + float3 positionLS = mul(shadowData.ShadowProj[cascadeIndex], float4(positionWS, 1)).xyz; + + if (all(positionLS.xy >= 0) && all(positionLS.xy <= 1)) { + shadowSample = ShadowCascadeMap.SampleCmpLevelZero(comparisonSampler, float3(positionLS.xy, cascadeIndex), positionLS.z); + } + + float fade = saturate(linearDepth / shadowData.EndSplitDistances.y); + float fadeFactor = 1.0 - pow(fade * fade, 8); + shadowSample = lerp(1.0, shadowSample, fadeFactor); + } + + uint bitmask = isValid ? outShadowBitmask[dtid] : 0; + bitmask &= ~(1u << bitIndex); + if (shadowSample > 0.5) + bitmask |= (1u << bitIndex); + + outShadowBitmask[dtid] = bitmask; + + uint validBits = min(shadowFrames, 32u); + float shadow = float(countbits(bitmask)) / float(validBits); + shadow = lerp(1.0, shadow, min(fadeInThreshold, shadowFrames) / fadeInThreshold); + outShadowVisibility[dtid] = shadow; + } } else if (!isValid) { outProbeArray[dtid] = unitSH; outAccumFramesArray[dtid] = 0; + outShadowBitmask[dtid] = 0; + outShadowVisibility[dtid] = 1.0; } -} \ No newline at end of file +} diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index 5a7740508f..6da8e99f8b 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -786,6 +786,12 @@ float GetSnowParameterY(float texProjTmp, float alpha) # undef SKYLIGHTING # endif +# if defined(SKYLIGHTING) +# if defined(RIM_LIGHTING) || defined(SOFT_LIGHTING) || defined(LOAD_SOFT_LIGHTING) || defined(BACK_LIGHTING) +# define SKYLIGHTING_SHADOW_VIS +# endif +# endif + # include "Common/LightingCommon.hlsli" # if defined(WATER_EFFECTS) @@ -2408,12 +2414,22 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # if defined(SKYLIGHTING) float3 positionMSSkylight = input.WorldPosition.xyz; +# if defined(SKYLIGHTING_SHADOW_VIS) + float skylightingShadowVisibility = 1.0; +# endif # if defined(DEFERRED) - sh2 skylightingSH = Skylighting::Sample(positionMSSkylight, worldNormal); + sh2 skylightingSH = Skylighting::Sample(positionMSSkylight, worldNormal +# if defined(SKYLIGHTING_SHADOW_VIS) + , skylightingShadowVisibility +# endif + ); # else - sh2 skylightingSH = inWorld ? Skylighting::Sample(positionMSSkylight, worldNormal) : Skylighting::UNIT_SH; + sh2 skylightingSH = inWorld ? Skylighting::Sample(positionMSSkylight, worldNormal +# if defined(SKYLIGHTING_SHADOW_VIS) + , skylightingShadowVisibility +# endif + ) : Skylighting::UNIT_SH; # endif - # endif float4 waterData = SharedData::GetWaterData(input.WorldPosition.xyz); @@ -2561,9 +2577,12 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float dirSoftShadow = 1.0; float dirVSMDetailedShadow = 1.0; -# if defined(VOLUMETRIC_SHADOWS) +# if defined(VOLUMETRIC_SHADOWS) && !(defined(DEFERRED) && defined(SKYLIGHTING_SHADOW_VIS)) if (inWorld && !inReflection && ShadowSampling::HasDirectionalShadows()) - dirSoftShadow = ShadowSampling::GetLightingShadow(input.WorldPosition.xyz, dirVSMDetailedShadow); +# if !defined(SKYLIGHTING_SHADOW_VIS) + dirSoftShadow = +# endif + ShadowSampling::GetLightingShadow(input.WorldPosition.xyz, dirVSMDetailedShadow); # endif float dirDetailedShadow = 1.0; @@ -2571,12 +2590,15 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) if ((Permutation::PixelShaderDescriptor & Permutation::LightingFlags::DefShadow) && (Permutation::PixelShaderDescriptor & Permutation::LightingFlags::ShadowDir)) { dirDetailedShadow *= shadowColor.x; -# if !defined(VOLUMETRIC_SHADOWS) +# if !defined(VOLUMETRIC_SHADOWS) && !defined(SKYLIGHTING_SHADOW_VIS) dirSoftShadow = dirDetailedShadow; # endif - } else { + } +# if !defined(DEFERRED) + else { dirDetailedShadow = dirVSMDetailedShadow; } +# endif # if defined(SCREEN_SPACE_SHADOWS) && defined(DEFERRED) if (!SharedData::InInterior && dirLightAngle >= 0.0) @@ -2626,6 +2648,10 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) } # endif +# if defined(SKYLIGHTING_SHADOW_VIS) + dirSoftShadow = skylightingShadowVisibility; +# endif + float3 diffuseColor = 0.0.xxx; float3 specularColor = 0.0.xxx; float3 transmissionColor = 0.0.xxx; diff --git a/package/Shaders/RunGrass.hlsl b/package/Shaders/RunGrass.hlsl index d41f5258de..7220729536 100644 --- a/package/Shaders/RunGrass.hlsl +++ b/package/Shaders/RunGrass.hlsl @@ -559,7 +559,8 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # if defined(SKYLIGHTING) float3 positionMSSkylight = input.WorldPosition.xyz; - float skylightingDiffuse = Skylighting::GetVertexSkylightingDiffuse(positionMSSkylight, normal, vertexAO); + sh2 skylightingSH = Skylighting::Sample(positionMSSkylight, normal); + float skylightingDiffuse = Skylighting::GetSkylightingDiffuse(skylightingSH, positionMSSkylight, normal, vertexAO); # endif // SKYLIGHTING float3 albedo = baseColor.xyz * vertexColor; @@ -820,7 +821,8 @@ PS_OUTPUT main(PS_INPUT input) # if defined(SKYLIGHTING) float3 positionMSSkylight = input.WorldPosition.xyz; - float skylightingDiffuse = Skylighting::GetVertexSkylightingDiffuse(positionMSSkylight, normal, vertexAO); + sh2 skylightingSH = Skylighting::Sample(positionMSSkylight, normal); + float skylightingDiffuse = Skylighting::GetSkylightingDiffuse(skylightingSH, positionMSSkylight, normal, vertexAO); # endif // SKYLIGHTING float3 directionalAmbientColor = Color::Ambient(max(0, SharedData::GetAmbient(normal))); diff --git a/src/Features/Skylighting.cpp b/src/Features/Skylighting.cpp index 6a44d86b0d..daf5a881bd 100644 --- a/src/Features/Skylighting.cpp +++ b/src/Features/Skylighting.cpp @@ -1,5 +1,6 @@ #include "Skylighting.h" +#include "Deferred.h" #include "I18n/I18n.h" #include "ShaderCache.h" #include "State.h" @@ -33,6 +34,11 @@ void Skylighting::ResetSkylighting() auto context = globals::d3d::context; UINT clr[1] = { 0 }; context->ClearUnorderedAccessViewUint(texAccumFramesArray->uav.get(), clr); + context->ClearUnorderedAccessViewUint(texShadowBitmask->uav.get(), clr); + + float clrf[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + context->ClearUnorderedAccessViewFloat(texShadowVisibility->uav.get(), clrf); + queuedResetSkylighting = false; } @@ -113,6 +119,18 @@ void Skylighting::SetupResources() texAccumFramesArray = new Texture3D(texDesc, "Skylighting::AccumFramesArray"); texAccumFramesArray->CreateSRV(srvDesc); texAccumFramesArray->CreateUAV(uavDesc); + + texDesc.Format = srvDesc.Format = uavDesc.Format = DXGI_FORMAT_R32_UINT; + + texShadowBitmask = new Texture3D(texDesc, "Skylighting::ShadowBitmask"); + texShadowBitmask->CreateSRV(srvDesc); + texShadowBitmask->CreateUAV(uavDesc); + + texDesc.Format = srvDesc.Format = uavDesc.Format = DXGI_FORMAT_R8_UNORM; + + texShadowVisibility = new Texture3D(texDesc, "Skylighting::ShadowVisibility"); + texShadowVisibility->CreateSRV(srvDesc); + texShadowVisibility->CreateUAV(uavDesc); } { @@ -220,9 +238,20 @@ void Skylighting::Prepass() auto context = globals::d3d::context; { - std::array srvs = { texOcclusion->srv.get() }; - std::array uavs = { texProbeArray->uav.get(), texAccumFramesArray->uav.get() }; - std::array samplers = { comparisonSampler.get() }; + std::array srvs = { + texOcclusion->srv.get(), + shadowCascadeSRV ? shadowCascadeSRV : nullptr, + shadowCascadeSRV ? globals::deferred->directionalShadowLights->srv.get() : nullptr + }; + std::array uavs = { + texProbeArray->uav.get(), + texAccumFramesArray->uav.get(), + texShadowBitmask->uav.get(), + texShadowVisibility->uav.get() + }; + std::array samplers = { + comparisonSampler.get() + }; // Update probe array { @@ -252,6 +281,9 @@ void Skylighting::Prepass() { ID3D11ShaderResourceView* srv = texProbeArray->srv.get(); context->PSSetShaderResources(50, 1, &srv); + + srv = texShadowVisibility->srv.get(); + context->PSSetShaderResources(53, 1, &srv); } } @@ -601,6 +633,16 @@ void Skylighting::RenderOcclusion() } } +void Skylighting::CaptureShadowCascadeSRV() +{ + auto context = globals::d3d::context; + ID3D11ShaderResourceView* srv = nullptr; + context->PSGetShaderResources(4, 1, &srv); + if (shadowCascadeSRV) + shadowCascadeSRV->Release(); + shadowCascadeSRV = srv; +} + void Skylighting::Main_Precipitation_RenderOcclusion::thunk() { globals::features::skylighting.RenderOcclusion(); diff --git a/src/Features/Skylighting.h b/src/Features/Skylighting.h index 78aa3ee0c6..767ec51996 100644 --- a/src/Features/Skylighting.h +++ b/src/Features/Skylighting.h @@ -72,6 +72,10 @@ struct Skylighting : Feature Texture2D* texOcclusion = nullptr; Texture3D* texProbeArray = nullptr; Texture3D* texAccumFramesArray = nullptr; + Texture3D* texShadowBitmask = nullptr; + Texture3D* texShadowVisibility = nullptr; + + ID3D11ShaderResourceView* shadowCascadeSRV = nullptr; winrt::com_ptr probeUpdateCompute = nullptr; @@ -87,6 +91,7 @@ struct Skylighting : Feature uint frameCount = 0; void ResetSkylighting(); + void CaptureShadowCascadeSRV(); std::chrono::time_point lastUpdateTimer = std::chrono::system_clock::now(); diff --git a/src/State.cpp b/src/State.cpp index fbc6d07892..8f577cfaf2 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -13,6 +13,7 @@ #include "Features/InteriorSun.h" #include "Features/PerformanceOverlay.h" #include "Features/Skin.h" +#include "Features/Skylighting.h" #include "Features/SkySync.h" #include "Features/TerrainBlending.h" #include "Features/TerrainHelper.h" @@ -59,6 +60,7 @@ void State::Draw() auto& truePBR = globals::features::truePBR; auto context = globals::d3d::context; auto& volumetricShadows = globals::features::volumetricShadows; + auto& skylighting = globals::features::skylighting; if (shaderCache->IsEnabled()) { // Process deferred cell transitions (interior detection) @@ -106,6 +108,8 @@ void State::Draw() volumetricShadows.CopyShadowLightData(); if (globals::features::exponentialHeightFog.loaded) globals::features::exponentialHeightFog.CaptureDirectionalShadowMap(); + if (skylighting.loaded) + skylighting.CaptureShadowCascadeSRV(); } } }