diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index 5798b507cb..0bee81d7d2 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -267,6 +267,13 @@ namespace SharedData float3 pad; }; + struct InteriorSunSettings + { + float EffectMeshSunInfluence; + uint IsInteriorWithSun; + float2 pad0; + }; + cbuffer FeatureData : register(b6) { GrassLightingSettings grassLightingSettings; @@ -285,6 +292,7 @@ namespace SharedData LinearLightingSettings linearLightingSettings; TerrainBlendingSettings terrainBlendingSettings; ExponentialHeightFogSettings exponentialHeightFogSettings; + InteriorSunSettings interiorSunSettings; }; Texture2D DepthTexture : register(t17); diff --git a/package/Shaders/Effect.hlsl b/package/Shaders/Effect.hlsl index 75f445a090..a265c03791 100644 --- a/package/Shaders/Effect.hlsl +++ b/package/Shaders/Effect.hlsl @@ -562,13 +562,15 @@ float3 GetLightingColor(float3 msPosition, float3 worldPosition, float2 screenPo float dirShadow = 1.0; const bool inWorld = (Permutation::ExtraShaderDescriptor & Permutation::ExtraFlags::InWorld); + const bool isInteriorSun = SharedData::interiorSunSettings.IsInteriorWithSun; - if (inWorld && !SharedData::InInterior) + if (inWorld && (!SharedData::InInterior || isInteriorSun)) dirShadow = ShadowSampling::Get3DFilteredShadow(worldPosition.xyz, viewDirection, screenPosition, eyeIndex, unusedSurfaceShadow); shadowVariance = 1.0 - sqrt(saturate(fwidth(dirShadow))); - dirColor *= dirShadow; + if (!isInteriorSun) + dirColor *= dirShadow; # if defined(EXP_HEIGHT_FOG) if (SharedData::exponentialHeightFogSettings.enabled) { @@ -584,6 +586,11 @@ float3 GetLightingColor(float3 msPosition, float3 worldPosition, float2 screenPo color = dirColor + ambientColor; + if (isInteriorSun) { + float influence = SharedData::interiorSunSettings.EffectMeshSunInfluence; + color *= lerp(1.0, dirShadow, influence); + } + # if defined(LIGHT_LIMIT_FIX) if (!(Permutation::ExtraShaderDescriptor & Permutation::ExtraFlags::InWorld)) # endif @@ -628,6 +635,7 @@ float3 GetLightingShadow(float3 color, float3 worldPosition, float2 screenPositi float shadow = 1.0; const bool inWorld = (Permutation::ExtraShaderDescriptor & Permutation::ExtraFlags::InWorld); + const bool isInteriorSun = SharedData::interiorSunSettings.IsInteriorWithSun; if (inWorld && !SharedData::InInterior) { shadow = 0.0; @@ -637,11 +645,15 @@ float3 GetLightingShadow(float3 color, float3 worldPosition, float2 screenPositi shadow += ShadowSampling::GetWorldShadow(samplePositionWS, FrameBuffer::CameraPosAdjust[eyeIndex].xyz, eyeIndex); } shadow *= rcpSampleCount; + } else if (inWorld && isInteriorSun) { + float unusedSurfaceShadow; + shadow = ShadowSampling::Get3DFilteredShadow(worldPosition, viewDirection, screenPosition, eyeIndex, unusedSurfaceShadow); } shadowVariance = 1.0 - sqrt(saturate(fwidth(shadow))); - dirColor *= shadow; + if (!isInteriorSun) + dirColor *= shadow; # if defined(EXP_HEIGHT_FOG) if (SharedData::exponentialHeightFogSettings.enabled) { @@ -649,7 +661,14 @@ float3 GetLightingShadow(float3 color, float3 worldPosition, float2 screenPositi } # endif - return dirColor + ambientColor; + color = dirColor + ambientColor; + + if (isInteriorSun) { + float influence = SharedData::interiorSunSettings.EffectMeshSunInfluence; + color *= lerp(1.0, shadow, influence); + } + + return color; } # endif diff --git a/package/Shaders/ISVolumetricLightingGenerateCS.hlsl b/package/Shaders/ISVolumetricLightingGenerateCS.hlsl index 7053aedfee..100a012ad9 100644 --- a/package/Shaders/ISVolumetricLightingGenerateCS.hlsl +++ b/package/Shaders/ISVolumetricLightingGenerateCS.hlsl @@ -86,7 +86,7 @@ cbuffer PerTechnique : register(b0) float shadowMapDepth = positionCSShifted.z; - bool noShadow = !SharedData::InInterior; + bool noShadow = !SharedData::InInterior || SharedData::interiorSunSettings.IsInteriorWithSun; if (EndSplitDistances.z >= shadowMapDepth) { uint cascadeIndex = ShadowMapCount >= 3.0f && shadowMapDepth > EndSplitDistances.y ? 2 : shadowMapDepth > EndSplitDistances.x ? 1 : 0; diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index 4beb5ad2cd..77332926f6 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -2496,7 +2496,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float dirVSMDetailedShadow = 1.0; # if defined(VOLUMETRIC_SHADOWS) - if (inWorld && !inReflection && !SharedData::InInterior) + if (inWorld && !inReflection) dirSoftShadow = ShadowSampling::GetLightingShadow(input.WorldPosition.xyz, eyeIndex, dirVSMDetailedShadow); # endif diff --git a/package/Shaders/RunGrass.hlsl b/package/Shaders/RunGrass.hlsl index 975ca38c3b..bce39a5aa9 100644 --- a/package/Shaders/RunGrass.hlsl +++ b/package/Shaders/RunGrass.hlsl @@ -597,8 +597,14 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float4 shadowColor = TexShadowMaskSampler.Load(int3(input.HPosition.xy, 0)); // Apply world shadow (terrain shadows, cloud shadows) directly to light color - if (!SharedData::InInterior) - dirLightColor *= ShadowSampling::GetWorldShadow(input.WorldPosition.xyz, FrameBuffer::CameraPosAdjust[eyeIndex].xyz, eyeIndex); + dirLightColor *= ShadowSampling::GetWorldShadow(input.WorldPosition.xyz, FrameBuffer::CameraPosAdjust[eyeIndex].xyz, eyeIndex); + + float dirSoftShadow = 1.0; + float dirVSMDetailedShadow = 1.0; + +# if defined(VOLUMETRIC_SHADOWS) + dirSoftShadow = ShadowSampling::GetLightingShadow(input.WorldPosition.xyz, eyeIndex, dirVSMDetailedShadow); +# endif float dirDetailedShadow = 1.0; @@ -832,8 +838,7 @@ PS_OUTPUT main(PS_INPUT input) float3 dirLightColor = Color::DirectionalLight(SharedData::DirLightColor.xyz / max(llDirLightMult, 1e-5), SharedData::linearLightingSettings.isDirLightLinear) * llDirLightMult; // Apply world shadow (terrain shadows, cloud shadows) directly to light color - if (!SharedData::InInterior) - dirLightColor *= ShadowSampling::GetWorldShadow(input.WorldPosition.xyz, FrameBuffer::CameraPosAdjust[eyeIndex].xyz, eyeIndex); + dirLightColor *= ShadowSampling::GetWorldShadow(input.WorldPosition.xyz, FrameBuffer::CameraPosAdjust[eyeIndex].xyz, eyeIndex); float dirDetailedShadow = 1.0; diff --git a/src/FeatureBuffer.cpp b/src/FeatureBuffer.cpp index f876879095..d97005fed7 100644 --- a/src/FeatureBuffer.cpp +++ b/src/FeatureBuffer.cpp @@ -8,6 +8,7 @@ #include "Features/GrassLighting.h" #include "Features/HairSpecular.h" #include "Features/IBL.h" +#include "Features/InteriorSun.h" #include "Features/LODBlending.h" #include "Features/LightLimitFix.h" #include "Features/LinearLighting.h" @@ -51,5 +52,6 @@ std::pair GetFeatureBufferData(bool a_inWorld) globals::features::extendedTranslucency.GetCommonBufferData(), globals::features::linearLighting.GetCommonBufferData(), globals::features::terrainBlending.settings, - globals::features::exponentialHeightFog.settings); + globals::features::exponentialHeightFog.settings, + globals::features::interiorSun.GetShaderSettings()); } \ No newline at end of file diff --git a/src/Features/InteriorSun.cpp b/src/Features/InteriorSun.cpp index b7965d1566..91b1f1c269 100644 --- a/src/Features/InteriorSun.cpp +++ b/src/Features/InteriorSun.cpp @@ -6,7 +6,8 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( InteriorSun::Settings, ForceDoubleSidedRendering, - InteriorShadowDistance) + InteriorShadowDistance, + EffectMeshSunInfluence) void InteriorSun::DrawSettings() { @@ -26,6 +27,13 @@ void InteriorSun::DrawSettings() "Sets the distance shadows are rendered at in interiors. " "Lower values provide higher quality shadows and improved performance but may cause distant interior spaces to light up incorrectly. "); } + ImGui::SliderFloat("Effect Mesh Sun Influence", &settings.EffectMeshSunInfluence, 0.0f, 100.0f, "%.0f%%", ImGuiSliderFlags_AlwaysClamp); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text( + "Controls how much sunlight and shadows affect effect meshes (particles, fires, etc.) in interiors. " + "At 100%%, effect meshes receive full sun shadow darkening. " + "At 0%%, effect meshes ignore sunlight entirely, matching vanilla interior behavior. "); + } } void InteriorSun::LoadSettings(json& o_json) @@ -216,6 +224,14 @@ bool InteriorSun::IsInSunDirectionAndWithinShadowDistance(const RE::NiPointer= -radius && (distance - radius) <= *gShadowDistance; } +InteriorSun::ShaderSettings InteriorSun::GetShaderSettings() const +{ + ShaderSettings data; + data.EffectMeshSunInfluence = std::clamp(settings.EffectMeshSunInfluence, 0.0f, 100.0f) * 0.01f; + data.IsInteriorWithSun = isInteriorWithSun ? 1u : 0u; + return data; +} + void InteriorSun::SetShadowDistance(bool inInterior) { using func_t = decltype(SetShadowDistance); diff --git a/src/Features/InteriorSun.h b/src/Features/InteriorSun.h index 9be4ef964a..6acae32511 100644 --- a/src/Features/InteriorSun.h +++ b/src/Features/InteriorSun.h @@ -37,10 +37,21 @@ struct InteriorSun : Feature { bool ForceDoubleSidedRendering = true; float InteriorShadowDistance = 5000; + float EffectMeshSunInfluence = 100.0f; }; Settings settings; + struct alignas(16) ShaderSettings + { + float EffectMeshSunInfluence = 1.0f; + uint32_t IsInteriorWithSun = 0; + float pad[2] = {}; + }; + STATIC_ASSERT_ALIGNAS_16(ShaderSettings); + + ShaderSettings GetShaderSettings() const; + std::atomic isInteriorWithSun = false; struct GetWorldSpace