diff --git a/package/Shaders/ISVolumetricLightingGenerateCS.hlsl b/package/Shaders/ISVolumetricLightingGenerateCS.hlsl index 01372dedb4..96f84aeff8 100644 --- a/package/Shaders/ISVolumetricLightingGenerateCS.hlsl +++ b/package/Shaders/ISVolumetricLightingGenerateCS.hlsl @@ -79,7 +79,7 @@ cbuffer PerTechnique : register(b0) { 0.001, 0.001, 0.001 } }; - float3 normalizedCoordinates = dispatchID.xyz * rcp(TextureDimensions.xyz); + float3 normalizedCoordinates = float3(dispatchID.xy + 0.5, dispatchID.z - 1.0) * rcp(TextureDimensions.xyz); float2 uv = normalizedCoordinates.xy; uint eyeIndex = Stereo::GetEyeIndexFromTexCoord(uv); float3 depthUv = Stereo::ConvertFromStereoUV(normalizedCoordinates, eyeIndex) + StepCoefficients[IterationIndex]; @@ -94,7 +94,7 @@ cbuffer PerTechnique : register(b0) float shadowMapDepth = positionCSShifted.z; - bool noShadow = true; + bool noShadow = !SharedData::InInterior; if (EndSplitDistances.z >= shadowMapDepth) { uint cascadeIndex = ShadowMapCount >= 3.0f && shadowMapDepth > EndSplitDistances.y ? 2 : shadowMapDepth > EndSplitDistances.x ? 1 : 0; float shadowMapThreshold = cascadeIndex == 0 ? 0.01f : 0.0f; diff --git a/package/Shaders/Utility.hlsl b/package/Shaders/Utility.hlsl index 9e286f3119..a6c4f1aaba 100644 --- a/package/Shaders/Utility.hlsl +++ b/package/Shaders/Utility.hlsl @@ -627,6 +627,9 @@ PS_OUTPUT main(PS_INPUT input) uint3 seed = Random::pcg3d(uint3(input.PositionCS.xy, input.PositionCS.x * Math::PI)); # if defined(RENDER_SHADOWMASK) + if(SharedData::InInterior) + shadowColor = float4(0,0,0,0); + if (EndSplitDistances.z >= shadowMapDepth) { float4x3 lightProjectionMatrix = ShadowMapProj[eyeIndex][0]; float shadowMapThreshold = AlphaTestRef.y; @@ -647,13 +650,13 @@ PS_OUTPUT main(PS_INPUT input) # if SHADOWFILTER == 0 float shadowMapValue = TexShadowMapSampler.Sample(SampShadowMapSampler, float3(positionLS.xy, cascadeIndex)).x; - if (shadowMapValue >= positionLS.z - shadowMapThreshold) { + if (shadowMapValue >= positionLS.z) { shadowVisibility = 1; } # elif SHADOWFILTER == 1 - shadowVisibility = TexShadowMapSamplerComp.SampleCmpLevelZero(SampShadowMapSamplerComp, float3(positionLS.xy, cascadeIndex), positionLS.z - shadowMapThreshold).x; + shadowVisibility = TexShadowMapSamplerComp.SampleCmpLevelZero(SampShadowMapSamplerComp, float3(positionLS.xy, cascadeIndex), positionLS.z).x; # elif SHADOWFILTER == 3 - shadowVisibility = GetPoissonDiskFilteredShadowVisibility(noise, rotationMatrix, TexShadowMapSamplerComp, SampShadowMapSamplerComp, positionLS.xy, cascadeIndex, positionLS.z - shadowMapThreshold, false); + shadowVisibility = GetPoissonDiskFilteredShadowVisibility(noise, rotationMatrix, TexShadowMapSamplerComp, SampShadowMapSamplerComp, positionLS.xy, cascadeIndex, positionLS.z, false); # endif if (cascadeIndex < 1 && StartSplitDistances.y < shadowMapDepth) { @@ -663,13 +666,13 @@ PS_OUTPUT main(PS_INPUT input) # if SHADOWFILTER == 0 float cascade1ShadowMapValue = TexShadowMapSampler.Sample(SampShadowMapSampler, float3(cascade1PositionLS.xy, 1)).x; - if (cascade1ShadowMapValue >= cascade1PositionLS.z - AlphaTestRef.z) { + if (cascade1ShadowMapValue >= cascade1PositionLS.z) { cascade1ShadowVisibility = 1; } # elif SHADOWFILTER == 1 - cascade1ShadowVisibility = TexShadowMapSamplerComp.SampleCmpLevelZero(SampShadowMapSamplerComp, float3(cascade1PositionLS.xy, 1), cascade1PositionLS.z - AlphaTestRef.z).x; + cascade1ShadowVisibility = TexShadowMapSamplerComp.SampleCmpLevelZero(SampShadowMapSamplerComp, float3(cascade1PositionLS.xy, 1), cascade1PositionLS.z).x; # elif SHADOWFILTER == 3 - cascade1ShadowVisibility = GetPoissonDiskFilteredShadowVisibility(noise, rotationMatrix, TexShadowMapSamplerComp, SampShadowMapSamplerComp, cascade1PositionLS.xy, 1, cascade1PositionLS.z - AlphaTestRef.z, false); + cascade1ShadowVisibility = GetPoissonDiskFilteredShadowVisibility(noise, rotationMatrix, TexShadowMapSamplerComp, SampShadowMapSamplerComp, cascade1PositionLS.xy, 1, cascade1PositionLS.z, false); # endif float cascade1BlendFactor = smoothstep(0, 1, (shadowMapDepth - StartSplitDistances.y) / (EndSplitDistances.x - StartSplitDistances.y)); @@ -692,7 +695,7 @@ PS_OUTPUT main(PS_INPUT input) shadowVisibility = min(shadowVisibility, lerp(1, focusShadowVisibility, focusShadowFade)); } - shadowColor.xyzw = fadeFactor * (shadowVisibility - 1) + 1; + shadowColor.xyzw = lerp(1.0 * !SharedData::InInterior, shadowVisibility, fadeFactor); } # elif defined(RENDER_SHADOWMASKSPOT) float4 positionLS = mul(transpose(ShadowMapProj[eyeIndex][0]), float4(positionMS.xyz, 1)); diff --git a/src/EngineFix.cpp b/src/EngineFix.cpp index 4ba9519ab6..f1d8e0a3f2 100644 --- a/src/EngineFix.cpp +++ b/src/EngineFix.cpp @@ -1,13 +1,16 @@ #include "EngineFix.h" #include "EngineFixes/ShadowmapCascadeCullingFix.h" +#include "EngineFixes/ShadowmapCascadeRasterizerFix.h" const std::vector& EngineFix::GetOnPostPostLoadFixesList() { static ShadowmapCascadeCullingFix shadowmapCascadeCullingFix; + static ShadowmapRasterizerFix shadowmapRasterizerFix; static std::vector fixes = { - &shadowmapCascadeCullingFix + &shadowmapCascadeCullingFix, + &shadowmapRasterizerFix }; return fixes; diff --git a/src/EngineFixes/ShadowmapCascadeRasterizerFix.cpp b/src/EngineFixes/ShadowmapCascadeRasterizerFix.cpp new file mode 100644 index 0000000000..629d4c8e8c --- /dev/null +++ b/src/EngineFixes/ShadowmapCascadeRasterizerFix.cpp @@ -0,0 +1,67 @@ +#include "ShadowmapCascadeRasterizerFix.h" + +void ShadowmapRasterizerFix::Install() +{ + // This function is called once per cascade to begin the updating and rendering process + stl::write_thunk_call(REL::RelocationID(101495, 108489).address() + REL::Relocate(0xC6, 0xC6)); + + gRasterStates = reinterpret_cast(REL::RelocationID(524748, 411363).address()); +} + +void ShadowmapRasterizerFix::BSShadowDirectionalLight_RenderShadowmaps_RenderCascade::thunk(RE::BSShadowDirectionalLight* light, void* arg1, void* arg2, uint32_t flags) +{ + static uint cascade = 0; + + static bool initialized = false; + if (!initialized) { + //Backup + if (cascade == 0) { + std::memcpy(backupGameRasterStates, *gRasterStates, sizeof(RasterStateArray)); + numCascades = std::min(RE::GetINISetting("iNumSplits:Display")->data.u, maxCascades); + } + + //Clone + CloneRasterStates(gRasterStates, cascade); + + initialized = cascade == numCascades - 1; + } + + //Emplace + std::memcpy(*gRasterStates, shadowmapRasterStates[cascade], sizeof(RasterStateArray)); + + func(light, arg1, arg2, flags); + + //Restore + if (cascade == numCascades - 1) + std::memcpy(*gRasterStates, backupGameRasterStates, sizeof(RasterStateArray)); + + cascade = ++cascade < numCascades ? cascade : 0; +} + +void ShadowmapRasterizerFix::GetUpdatedRasterDesc(D3D11_RASTERIZER_DESC& outputDesc, ShadowMapRasterizerDescriptor shadowmapDesc) +{ + outputDesc.DepthBias = shadowmapDesc.rasterDepthBias; + outputDesc.DepthBiasClamp = shadowmapDesc.rasterDepthBiasClamp; + outputDesc.SlopeScaledDepthBias = shadowmapDesc.rasterSlopeScaleBias; +} + +// Since state objects are shared globally across the pipeline we make duplicate arrays that cover the same range of states the game does +void ShadowmapRasterizerFix::CloneRasterStates(RasterStateArray* inputArray, int cascade) +{ + for (int fill = 0; fill < 2; fill++) { + for (int cull = 0; cull < 3; cull++) { + for (int depth = 0; depth < 12; depth++) { + for (int scissor = 0; scissor < 2; scissor++) { + if (auto* gRasterizer = (*inputArray)[fill][cull][depth][scissor]) { + D3D11_RASTERIZER_DESC desc{}; + gRasterizer->GetDesc(&desc); + + GetUpdatedRasterDesc(desc, cascadeDescriptors[cascade]); + + DX::ThrowIfFailed(globals::d3d::device->CreateRasterizerState(&desc, &shadowmapRasterStates[cascade][fill][cull][depth][scissor])); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/EngineFixes/ShadowmapCascadeRasterizerFix.h b/src/EngineFixes/ShadowmapCascadeRasterizerFix.h new file mode 100644 index 0000000000..0405e0c14f --- /dev/null +++ b/src/EngineFixes/ShadowmapCascadeRasterizerFix.h @@ -0,0 +1,51 @@ +#pragma once + +// This overrides the shadow cascade rasterizers to fix issues with peter panning and self shadowing +struct ShadowmapRasterizerFix : EngineFix +{ + std::string GetName() override { return "Shadowmap Cascade Rasterizer Fix"; } + void Install() override; + + using RasterStateArray = ID3D11RasterizerState* [2][3][12][2]; + + static void CloneRasterStates(RasterStateArray* inputArray, int cascade); + + static constexpr uint maxCascades = 3; + static inline uint numCascades = 0; + + static inline RasterStateArray* gRasterStates = nullptr; + static inline RasterStateArray backupGameRasterStates = {}; + static inline RasterStateArray shadowmapRasterStates[maxCascades] = {}; + + static constexpr int firstCascadeDepthBias = 160; + static constexpr float firstCascadeDepthBiasClamp = 0.015f; + static constexpr float firstCascadeSlopeScaleBias = 3.2f; + + static constexpr int secondCascadeDepthBias = 100; + static constexpr float secondCascadeDepthBiasClamp = 0.015f; + static constexpr float secondCascadeSlopeScaleBias = 3.8f; + + static constexpr int thirdCascadeDepthBias = 100; + static constexpr float thirdCascadeDepthBiasClamp = 0.015f; + static constexpr float thirdCascadeSlopeScaleBias = 3.8f; + + struct ShadowMapRasterizerDescriptor + { + int rasterDepthBias; + float rasterDepthBiasClamp; + float rasterSlopeScaleBias; + }; + static void GetUpdatedRasterDesc(D3D11_RASTERIZER_DESC& outputDesc, ShadowMapRasterizerDescriptor desc); + + static constexpr ShadowMapRasterizerDescriptor cascadeDescriptors[maxCascades] = { + { firstCascadeDepthBias, firstCascadeDepthBiasClamp, firstCascadeSlopeScaleBias }, + { secondCascadeDepthBias, secondCascadeDepthBiasClamp, secondCascadeSlopeScaleBias }, + { thirdCascadeDepthBias, thirdCascadeDepthBiasClamp, thirdCascadeSlopeScaleBias } + }; + + struct BSShadowDirectionalLight_RenderShadowmaps_RenderCascade + { + static void thunk(RE::BSShadowDirectionalLight* light, void* arg1, void* arg2, uint32_t flags); + static inline REL::Relocation func; + }; +};