// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #ifndef __EXPONENTIAL_HEIGHT_FOG__ #define __EXPONENTIAL_HEIGHT_FOG__ #include "./Flax/Common.hlsl" #include "./Flax/Math.hlsl" // Structure that contains information about exponential height fog struct ExponentialHeightFogData { float3 FogInscatteringColor; float FogMinOpacity; float FogDensity; float FogHeight; float FogHeightFalloff; float FogAtViewPosition; float3 InscatteringLightDirection; float ApplyDirectionalInscattering; float3 DirectionalInscatteringColor; float DirectionalInscatteringExponent; float FogCutoffDistance; float VolumetricFogMaxDistance; float DirectionalInscatteringStartDistance; float StartDistance; }; float4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, float3 posWS, float3 camWS, float skipDistance) { float3 cameraToPos = posWS - camWS; float cameraToPosSqr = dot(cameraToPos, cameraToPos); float cameraToPosLenInv = rsqrt(cameraToPosSqr); float cameraToPosLen = cameraToPosSqr * cameraToPosLenInv; float3 cameraToReceiverNorm = cameraToPos * cameraToPosLenInv; float rayOriginTerms = exponentialHeightFog.FogAtViewPosition; float rayLength = cameraToPosLen; float rayDirectionY = cameraToPos.y; // Apply start distance offset skipDistance = max(skipDistance, exponentialHeightFog.StartDistance); if (skipDistance > 0) { float excludeIntersectionTime = skipDistance * cameraToPosLenInv; float cameraToExclusionIntersectionY = excludeIntersectionTime * cameraToPos.y; float exclusionIntersectionY = camWS.y + cameraToExclusionIntersectionY; rayLength = (1.0f - excludeIntersectionTime) * cameraToPosLen; rayDirectionY = cameraToPos.y - cameraToExclusionIntersectionY; float exponent = exponentialHeightFog.FogHeightFalloff * (exclusionIntersectionY - exponentialHeightFog.FogHeight); rayOriginTerms = exponentialHeightFog.FogDensity * exp2(-exponent); } // Calculate the integral of the ray starting from the view to the object position with the fog density function float falloff = max(-127.0f, exponentialHeightFog.FogHeightFalloff * rayDirectionY); float lineIntegral = (1.0f - exp2(-falloff)) / falloff; float lineIntegralTaylor = log(2.0f) - (0.5f * Pow2(log(2.0f))) * falloff; float exponentialHeightLineIntegralCalc = rayOriginTerms * (abs(falloff) > 0.01f ? lineIntegral : lineIntegralTaylor); float exponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * rayLength; // Calculate the light that went through the fog float expFogFactor = max(saturate(exp2(-exponentialHeightLineIntegral)), exponentialHeightFog.FogMinOpacity); // Calculate the directional light inscattering float3 inscatteringColor = exponentialHeightFog.FogInscatteringColor; float3 directionalInscattering = 0; BRANCH if (exponentialHeightFog.ApplyDirectionalInscattering > 0) { float3 directionalLightInscattering = exponentialHeightFog.DirectionalInscatteringColor * pow(saturate(dot(cameraToReceiverNorm, exponentialHeightFog.InscatteringLightDirection)), exponentialHeightFog.DirectionalInscatteringExponent); float dirExponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * max(rayLength - exponentialHeightFog.DirectionalInscatteringStartDistance, 0.0f); float directionalInscatteringFogFactor = saturate(exp2(-dirExponentialHeightLineIntegral)); directionalInscattering = directionalLightInscattering * (1.0f - directionalInscatteringFogFactor); } // Disable fog after a certain distance FLATTEN if (exponentialHeightFog.FogCutoffDistance > 0 && cameraToPosLen > exponentialHeightFog.FogCutoffDistance) { expFogFactor = 1; directionalInscattering = 0; } return float4(inscatteringColor * (1.0f - expFogFactor) + directionalInscattering, expFogFactor); } #endif