diff --git a/features/Exponential Height Fog/Shaders/ExponentialHeightFog/ExponentialHeightFog.hlsli b/features/Exponential Height Fog/Shaders/ExponentialHeightFog/ExponentialHeightFog.hlsli index 824285b489..c2e6bba23f 100644 --- a/features/Exponential Height Fog/Shaders/ExponentialHeightFog/ExponentialHeightFog.hlsli +++ b/features/Exponential Height Fog/Shaders/ExponentialHeightFog/ExponentialHeightFog.hlsli @@ -14,6 +14,20 @@ namespace ExponentialHeightFog return SharedData::exponentialHeightFogSettings.respectVanillaFogFade != 0 ? vanillaFogFade : 1.0f; } + bool ShouldDisableVanillaFog() + { + return SharedData::exponentialHeightFogSettings.enabled && SharedData::exponentialHeightFogSettings.disableVanillaFog != 0; + } + + // Henyey-Greenstein phase function for physically-based inscattering. + // g: asymmetry parameter [-1, 1]. Positive = forward scattering, 0 = isotropic. + float HenyeyGreenstein(float cosTheta, float g) + { + float g2 = g * g; + float denom = 1.0f + g2 - 2.0f * g * cosTheta; + return (1.0f - g2) / (4.0f * Math::PI * pow(max(denom, 1e-5f), 1.5f)); + } + float4 GetExponentialHeightFog(float3 positionWS, float3 cameraWS, float3 fogColor) { float fogHeightFalloff = SharedData::exponentialHeightFogSettings.fogHeightFalloff * 0.001f; @@ -47,19 +61,25 @@ namespace ExponentialHeightFog float expFogFactor = saturate(exp2(-exponentialHeightLineIntegral)); + float3 fogInscatteringColor = fogColor * SharedData::exponentialHeightFogSettings.originalFogColorAmount; + fogInscatteringColor += SharedData::exponentialHeightFogSettings.fogInscatteringColor.rgb * SharedData::exponentialHeightFogSettings.fogInscatteringColor.a; + #if defined(DYNAMIC_CUBEMAPS) if (SharedData::exponentialHeightFogSettings.useDynamicCubemaps > 0) { - float3 tintColor = lerp(fogColor, SharedData::exponentialHeightFogSettings.inscatteringTint.xyz, SharedData::exponentialHeightFogSettings.inscatteringTint.w); float3 cubemapColor = DynamicCubemaps::EnvReflectionsTexture.SampleLevel(SampColorSampler, normalize(lerp(positionWS, float3(0, 0, 1), saturate((SharedData::exponentialHeightFogSettings.cubemapMipLevel + 1) / 8))), SharedData::exponentialHeightFogSettings.cubemapMipLevel).xyz; - fogColor = tintColor * cubemapColor * (1.0f - expFogFactor); + fogInscatteringColor += cubemapColor * SharedData::exponentialHeightFogSettings.inscatteringTint.rgb * SharedData::exponentialHeightFogSettings.inscatteringTint.a; } #endif + fogColor = fogInscatteringColor * (1.0f - expFogFactor); + float3 directionalInscattering = 0; - // Calculate directional light inscattering + // Calculate directional light inscattering using Henyey-Greenstein phase function if (SharedData::exponentialHeightFogSettings.directionalInscatteringMultiplier > 0) { - float3 directionalLightInscattering = SharedData::DirLightColor.xyz * pow(saturate(dot(normalize(positionWS), SharedData::DirLightDirection.xyz)), SharedData::exponentialHeightFogSettings.directionalInscatteringExponent) / (2 * Math::TAU); + float cosTheta = dot(normalize(positionWS), SharedData::DirLightDirection.xyz); + float phase = HenyeyGreenstein(cosTheta, SharedData::exponentialHeightFogSettings.directionalInscatteringAnisotropy); + float3 directionalLightInscattering = SharedData::DirLightColor.xyz * phase; float dirExponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * max(rayLength - SharedData::exponentialHeightFogSettings.startDistance, 0); float dirExpFogFactor = saturate(exp2(-dirExponentialHeightLineIntegral)); directionalInscattering = directionalLightInscattering * (1 - dirExpFogFactor) * SharedData::exponentialHeightFogSettings.directionalInscatteringMultiplier; @@ -83,17 +103,16 @@ namespace ExponentialHeightFog float3 lightDir = SharedData::DirLightDirection.xyz; float lightDirZ = lightDir.z; - float exponentialHeightLineIntegral = 0.0f; + float sunlightFogAttenuation = 0.0f; // Integral = Density * (1 - exp2(-slope * inf)) / slope if (lightDirZ > 0.001f) { float slope = max(fogHeightFalloff * lightDirZ, 1e-8f); - exponentialHeightLineIntegral = localDensity / slope; - } else { - return 0.0f; + float exponentialHeightLineIntegral = localDensity / slope; + sunlightFogAttenuation = saturate(exp2(-exponentialHeightLineIntegral)); } - return saturate(exp2(-exponentialHeightLineIntegral)); + return lerp(1.0f, sunlightFogAttenuation, SharedData::exponentialHeightFogSettings.sunlightAttenuationAmount); } } #endif diff --git a/features/Exponential Height Fog/Shaders/Features/ExponentialHeightFog.ini b/features/Exponential Height Fog/Shaders/Features/ExponentialHeightFog.ini index 0bc0292971..9e325f8475 100644 --- a/features/Exponential Height Fog/Shaders/Features/ExponentialHeightFog.ini +++ b/features/Exponential Height Fog/Shaders/Features/ExponentialHeightFog.ini @@ -1,5 +1,5 @@ [Info] -Version = 1-1-0 +Version = 1-2-0 [Nexus] autoupload = false diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index e64cbd3f5b..7aa3612a71 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -257,11 +257,15 @@ namespace SharedData float fogHeightFalloff; float fogDensity; float directionalInscatteringMultiplier; - float directionalInscatteringExponent; + float directionalInscatteringAnisotropy; float4 inscatteringTint; float cubemapMipLevel; + float sunlightAttenuationAmount; uint respectVanillaFogFade; - float2 pad; + uint disableVanillaFog; + float4 fogInscatteringColor; + float originalFogColorAmount; + float3 pad; }; cbuffer FeatureData : register(b6) diff --git a/package/Shaders/Effect.hlsl b/package/Shaders/Effect.hlsl index 64894c3b2f..75f445a090 100644 --- a/package/Shaders/Effect.hlsl +++ b/package/Shaders/Effect.hlsl @@ -854,8 +854,12 @@ PS_OUTPUT main(PS_INPUT input) } # endif # if defined(EXP_HEIGHT_FOG) + float vanillaFogFactor = fogFactor; + float3 vanillaFogColor = fogColor; + float expFogFactor = 0; if (SharedData::exponentialHeightFogSettings.enabled) { float4 exponentialHeightFog = ExponentialHeightFog::GetExponentialHeightFog(input.WorldPosition.xyz, FrameBuffer::CameraPosAdjust[eyeIndex].xyz, fogColor); + expFogFactor = exponentialHeightFog.w; # if defined(ADDBLEND) || defined(MULTBLEND) || defined(MULTBLEND_DECAL) fogColor = exponentialHeightFog.xyz; fogFactor = exponentialHeightFog.w; @@ -863,14 +867,30 @@ PS_OUTPUT main(PS_INPUT input) fogColor = lightColor; alpha *= 1 - exponentialHeightFog.w; # endif + if (ExponentialHeightFog::ShouldDisableVanillaFog()) { + vanillaFogFactor = 0; + } } # endif # if defined(ADDBLEND) +# if defined(EXP_HEIGHT_FOG) + float3 blendedColor = lightColor * (1 - vanillaFogFactor) * (1 - expFogFactor); +# else float3 blendedColor = lightColor * (1 - fogFactor); +# endif # elif defined(MULTBLEND) || defined(MULTBLEND_DECAL) +# if defined(EXP_HEIGHT_FOG) + float3 blendedColor = lerp(lightColor, 1.0.xxx, saturate(1.5 * vanillaFogFactor).xxx); + blendedColor = lerp(blendedColor, 1.0.xxx, saturate(1.5 * expFogFactor).xxx); +# else float3 blendedColor = lerp(lightColor, 1.0.xxx, saturate(1.5 * fogFactor).xxx); +# endif # else +# if defined(EXP_HEIGHT_FOG) + float3 blendedColor = lerp(lightColor, vanillaFogColor, vanillaFogFactor.xxx); +# else float3 blendedColor = lerp(lightColor, fogColor, fogFactor.xxx); +# endif # endif # else float3 blendedColor = lightColor.xyz; diff --git a/package/Shaders/ISSAOComposite.hlsl b/package/Shaders/ISSAOComposite.hlsl index 80ace670b2..55785c7604 100644 --- a/package/Shaders/ISSAOComposite.hlsl +++ b/package/Shaders/ISSAOComposite.hlsl @@ -194,15 +194,23 @@ PS_OUTPUT main(PS_INPUT input) float4 positionWS = float4(2 * float2(monoUV.x, -monoUV.y + 1) - 1, depth, 1); positionWS = mul(FrameBuffer::CameraViewProjInverse[eyeIndex], positionWS); positionWS.xyz = positionWS.xyz / positionWS.w; + float4 exponentialHeightFog = (float4)0; if (exponentialHeightFogEnabled) { - float4 exponentialHeightFog = ExponentialHeightFog::GetExponentialHeightFog(positionWS.xyz, FrameBuffer::CameraPosAdjust[eyeIndex].xyz, fogColor); - fogColor = exponentialHeightFog.xyz; - fogFactor = exponentialHeightFog.w; + exponentialHeightFog = ExponentialHeightFog::GetExponentialHeightFog(positionWS.xyz, FrameBuffer::CameraPosAdjust[eyeIndex].xyz, fogColor); } if (isGeometryDepth || exponentialHeightFogEnabled) { float fogFade = exponentialHeightFogEnabled ? ExponentialHeightFog::GetVanillaFogFade(FogNearColor.w) : FogNearColor.w; float3 fogSource = exponentialHeightFogEnabled && !isGeometryDepth ? composedColor.xyz : fogFade * composedColor.xyz; - composedColor.xyz = lerp(fogSource, fogFade * fogColor, fogFactor); + if (exponentialHeightFogEnabled && !ExponentialHeightFog::ShouldDisableVanillaFog()) { + // Apply vanilla fog first, then exp fog on top + composedColor.xyz = lerp(fogSource, fogFade * fogColor, Color::FogAlpha(fogFactor)); + composedColor.xyz = lerp(composedColor.xyz, fogFade * exponentialHeightFog.xyz, exponentialHeightFog.w); + } else if (exponentialHeightFogEnabled) { + // Disable vanilla fog, only apply exp height fog + composedColor.xyz = lerp(fogSource, fogFade * exponentialHeightFog.xyz, exponentialHeightFog.w); + } else { + composedColor.xyz = lerp(fogSource, fogFade * fogColor, Color::FogAlpha(fogFactor)); + } } # else if (isGeometryDepth) { diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index 1fed3c69c6..27caf74df4 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -3087,14 +3087,28 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) } # endif # if defined(EXP_HEIGHT_FOG) + float3 vanillaFogColor = fogColor; + float vanillaFogFactor = fogFactor; if (SharedData::exponentialHeightFogSettings.enabled) { float4 exponentialHeightFog = ExponentialHeightFog::GetExponentialHeightFog(input.WorldPosition.xyz, FrameBuffer::CameraPosAdjust[eyeIndex].xyz, fogColor); fogColor = exponentialHeightFog.xyz; fogFactor = exponentialHeightFog.w; } # endif - if (FrameBuffer::FrameParams.y && FrameBuffer::FrameParams.z) + if (FrameBuffer::FrameParams.y && FrameBuffer::FrameParams.z) { +# if defined(EXP_HEIGHT_FOG) + if (SharedData::exponentialHeightFogSettings.enabled) { + if (!ExponentialHeightFog::ShouldDisableVanillaFog()) { + color.xyz = lerp(color.xyz, vanillaFogColor, vanillaFogFactor); + } + color.xyz = lerp(color.xyz, fogColor, fogFactor); + } else { + color.xyz = lerp(color.xyz, fogColor, fogFactor); + } +# else color.xyz = lerp(color.xyz, fogColor, fogFactor); +# endif + } # endif # if defined(TESTCUBEMAP) && defined(ENVMAP) && defined(DYNAMIC_CUBEMAPS) diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index 631a602b4c..87927eaece 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -1279,13 +1279,26 @@ PS_OUTPUT main(PS_INPUT input) # if defined(EXP_HEIGHT_FOG) if (SharedData::exponentialHeightFogSettings.enabled) { float4 exponentialHeightFog = ExponentialHeightFog::GetExponentialHeightFog(input.WPosition.xyz, FrameBuffer::CameraPosAdjust[eyeIndex].xyz, fogColor); - fogColor = exponentialHeightFog.xyz; - fogDistanceFactor = exponentialHeightFog.w; + if (ExponentialHeightFog::ShouldDisableVanillaFog()) { + fogColor = exponentialHeightFog.xyz; + fogColor *= GetWaterFogFade(eyeIndex); + finalColorPreFog = lerp(finalColorPreFog, fogColor, exponentialHeightFog.w); + } else { + fogColor *= GetWaterFogFade(eyeIndex); + finalColorPreFog = lerp(finalColorPreFog, fogColor, fogDistanceFactor); + float3 expFogColor = exponentialHeightFog.xyz * GetWaterFogFade(eyeIndex); + finalColorPreFog = lerp(finalColorPreFog, expFogColor, exponentialHeightFog.w); + } + } else { + fogColor *= GetWaterFogFade(eyeIndex); + finalColorPreFog = lerp(finalColorPreFog, fogColor, fogDistanceFactor); } -# endif +# else fogColor *= GetWaterFogFade(eyeIndex); + finalColorPreFog = lerp(finalColorPreFog, fogColor, fogDistanceFactor); +# endif - float3 finalColor = lerp(finalColorPreFog, fogColor, fogDistanceFactor); + float3 finalColor = finalColorPreFog; # if defined(WETNESS_EFFECTS) && defined(DEBUG_WETNESS_EFFECTS) // DEBUG MODE: Override water color with debug visualization @@ -1317,20 +1330,32 @@ PS_OUTPUT main(PS_INPUT input) # if defined(EXP_HEIGHT_FOG) if (SharedData::exponentialHeightFogSettings.enabled) { float4 exponentialHeightFog = ExponentialHeightFog::GetExponentialHeightFog(input.WPosition.xyz, FrameBuffer::CameraPosAdjust[eyeIndex].xyz, preFogColor); - preFogColor = exponentialHeightFog.xyz; - fogDistanceFactor = exponentialHeightFog.w; + if (ExponentialHeightFog::ShouldDisableVanillaFog()) { + preFogColor = exponentialHeightFog.xyz; + preFogColor *= GetWaterFogFade(eyeIndex); + finalColorPreFog = lerp(finalColorPreFog, preFogColor, exponentialHeightFog.w); + } else { + preFogColor *= GetWaterFogFade(eyeIndex); + finalColorPreFog = lerp(finalColorPreFog, preFogColor, fogDistanceFactor); + float3 expFogColor = exponentialHeightFog.xyz * GetWaterFogFade(eyeIndex); + finalColorPreFog = lerp(finalColorPreFog, expFogColor, exponentialHeightFog.w); + } + } else { + preFogColor *= GetWaterFogFade(eyeIndex); + finalColorPreFog = lerp(finalColorPreFog, preFogColor, fogDistanceFactor); } -# endif +# else preFogColor *= GetWaterFogFade(eyeIndex); finalColorPreFog = lerp(finalColorPreFog, preFogColor, fogDistanceFactor); +# endif float3 refractionColor = diffuseOutput.refractionColor; float fogFactor = min(FogParam.w, pow(saturate(-diffuseOutput.depth * FogParam.y - FogParam.x), FogParam.z)); float3 fogColor = Color::Fog(lerp(FogNearColor.xyz, FogFarColor.xyz, fogFactor)); # if defined(EXP_HEIGHT_FOG) - if (SharedData::exponentialHeightFogSettings.enabled) { + if (SharedData::exponentialHeightFogSettings.enabled && ExponentialHeightFog::ShouldDisableVanillaFog()) { fogFactor = 0; } # endif diff --git a/src/Features/ExponentialHeightFog.cpp b/src/Features/ExponentialHeightFog.cpp index 26b067fa53..ddf40c2e3f 100644 --- a/src/Features/ExponentialHeightFog.cpp +++ b/src/Features/ExponentialHeightFog.cpp @@ -11,10 +11,14 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( fogHeightFalloff, fogDensity, directionalInscatteringMultiplier, - directionalInscatteringExponent, + directionalInscatteringAnisotropy, inscatteringTint, cubemapMipLevel, - respectVanillaFogFade) + sunlightAttenuationAmount, + respectVanillaFogFade, + disableVanillaFog, + fogInscatteringColor, + originalFogColorAmount) void ExponentialHeightFog::RestoreDefaultSettings() { @@ -37,9 +41,22 @@ void ExponentialHeightFog::DrawSettings() Util::WeatherUI::SliderFloat("Start Distance", this, "startDistance", &settings.startDistance, 0.0f, 100000.0f, "%.1f"); Util::WeatherUI::SliderFloat("Fog Height", this, "fogHeight", &settings.fogHeight, -22000.0f, 22000.0f, "%.1f"); Util::WeatherUI::SliderFloat("Fog Height Falloff", this, "fogHeightFalloff", &settings.fogHeightFalloff, 0.001f, 2.0f, "%.3f"); + Util::WeatherUI::ColorEdit4("Fog Inscattering Color", this, "fogInscatteringColor", (float*)&settings.fogInscatteringColor); + Util::WeatherUI::SliderFloat("Original Fog Color Amount", this, "originalFogColorAmount", &settings.originalFogColorAmount, 0.0f, 1.0f, "%.2f"); Util::WeatherUI::SliderFloat("Fog Density", this, "fogDensity", &settings.fogDensity, 0.0f, 1.0f, "%.3f"); Util::WeatherUI::SliderFloat("Directional Light Inscattering Multiplier", this, "directionalInscatteringMultiplier", &settings.directionalInscatteringMultiplier, 0.0f, 10.0f, "%.2f"); - Util::WeatherUI::SliderFloat("Directional Light Inscattering Exponent", this, "directionalInscatteringExponent", &settings.directionalInscatteringExponent, 1.0f, 128.0f, "%.2f"); + Util::WeatherUI::SliderFloat("Sunlight Attenuation Amount", this, "sunlightAttenuationAmount", &settings.sunlightAttenuationAmount, 0.0f, 1.0f, "%.2f"); + Util::WeatherUI::SliderFloat("Directional Light Inscattering Anisotropy", this, "directionalInscatteringAnisotropy", &settings.directionalInscatteringAnisotropy, -0.99f, 0.99f, "%.3f"); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text( + "Controls the asymmetry of inscattering via the Henyey-Greenstein phase function.\n" + "Positive values produce forward scattering (glow around sun).\n" + "Zero is isotropic. Negative values produce back scattering."); + } + ImGui::Checkbox("Disable Vanilla Fog", (bool*)&settings.disableVanillaFog); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Disables the vanilla fog entirely. Only exponential height fog will be applied."); + } Util::WeatherUI::Checkbox("Apply Vanilla Fade", this, "respectVanillaFogFade", (bool*)&settings.respectVanillaFogFade); if (auto _tt = Util::HoverTooltipWrapper()) { ImGui::Text("Applies vanilla fade brightness to exponential height fog."); @@ -76,6 +93,21 @@ void ExponentialHeightFog::RegisterWeatherVariables() 0.2f, 0.001f, 2.0f)); + registry->RegisterVariable(std::make_shared( + "Fog Inscattering Color", + "fogInscatteringColor", + "Color added to the fog inscattering contribution", + &settings.fogInscatteringColor, + float4{ 0.0f, 0.0f, 0.0f, 1.0f })); + + registry->RegisterVariable(std::make_shared( + "Original Fog Color Amount", + "originalFogColorAmount", + "Amount of the original fog color added to fog inscattering", + &settings.originalFogColorAmount, + 1.0f, + 0.0f, 1.0f)); + registry->RegisterVariable(std::make_shared( "Fog Density", "fogDensity", @@ -93,12 +125,20 @@ void ExponentialHeightFog::RegisterWeatherVariables() 0.0f, 10.0f)); registry->RegisterVariable(std::make_shared( - "Directional Inscattering Exponent", - "directionalInscatteringExponent", - "Controls the size of the directional inscattering cone", - &settings.directionalInscatteringExponent, - 4.0f, - 1.0f, 128.0f)); + "Sunlight Attenuation Amount", + "sunlightAttenuationAmount", + "Amount of fog attenuation applied to direct sunlight", + &settings.sunlightAttenuationAmount, + 1.0f, + 0.0f, 1.0f)); + + registry->RegisterVariable(std::make_shared( + "Directional Inscattering Anisotropy", + "directionalInscatteringAnisotropy", + "Henyey-Greenstein asymmetry parameter. Positive = forward scattering, 0 = isotropic, negative = back scattering.", + &settings.directionalInscatteringAnisotropy, + 0.7f, + -0.99f, 0.99f)); registry->RegisterVariable(std::make_shared( "Inscattering Cubemap Tint", @@ -116,4 +156,14 @@ void ExponentialHeightFog::RegisterWeatherVariables() [](const bool& from, const bool& to, float factor) { return factor > 0.5f ? to : from; })); + + registry->RegisterVariable(std::make_shared>( + "disableVanillaFog", + "Disable Vanilla Fog", + "Disables vanilla fog entirely, only exponential height fog is applied", + (bool*)&settings.disableVanillaFog, + false, + [](const bool& from, const bool& to, float factor) { + return factor > 0.5f ? to : from; + })); } diff --git a/src/Features/ExponentialHeightFog.h b/src/Features/ExponentialHeightFog.h index 36e36c64bc..3b611b7e73 100644 --- a/src/Features/ExponentialHeightFog.h +++ b/src/Features/ExponentialHeightFog.h @@ -40,11 +40,15 @@ struct ExponentialHeightFog : Feature float fogHeightFalloff = 0.2f; float fogDensity = 0.02f; float directionalInscatteringMultiplier = 1.0f; - float directionalInscatteringExponent = 4.0f; + float directionalInscatteringAnisotropy = 0.7f; float4 inscatteringTint = { 1.0f, 1.0f, 1.0f, 1.0f }; float cubemapMipLevel = 3.0f; + float sunlightAttenuationAmount = 1.0f; uint respectVanillaFogFade = 0; - float pad[2]; + uint disableVanillaFog = 0; + float4 fogInscatteringColor = { 0.0f, 0.0f, 0.0f, 1.0f }; + float originalFogColorAmount = 1.0f; + float3 pad; } settings; - static_assert(sizeof(Settings) == sizeof(float4) * 4, "Settings must match HLSL ExponentialHeightFogSettings."); + static_assert(sizeof(Settings) == sizeof(float4) * 6, "Settings must match HLSL ExponentialHeightFogSettings."); };