103 lines
4.5 KiB
HLSL
103 lines
4.5 KiB
HLSL
// Copyright (c) 2012-2020 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;
|
|
};
|
|
|
|
half4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, float3 worldPosition, float3 cameraPosition, float excludeDistance)
|
|
{
|
|
float3 cameraToReceiver = worldPosition - cameraPosition;
|
|
float cameraToReceiverLengthSqr = dot(cameraToReceiver, cameraToReceiver);
|
|
float cameraToReceiverLengthInv = rsqrt(cameraToReceiverLengthSqr);
|
|
float cameraToReceiverLength = cameraToReceiverLengthSqr * cameraToReceiverLengthInv;
|
|
half3 cameraToReceiverNormalized = cameraToReceiver * cameraToReceiverLengthInv;
|
|
|
|
float rayOriginTerms = exponentialHeightFog.FogAtViewPosition;
|
|
float rayLength = cameraToReceiverLength;
|
|
float rayDirectionY = cameraToReceiver.y;
|
|
|
|
// Apply start distance offset
|
|
excludeDistance = max(excludeDistance, exponentialHeightFog.StartDistance);
|
|
if (excludeDistance > 0)
|
|
{
|
|
float excludeIntersectionTime = excludeDistance * cameraToReceiverLengthInv;
|
|
float cameraToExclusionIntersectionY = excludeIntersectionTime * cameraToReceiver.y;
|
|
float exclusionIntersectionY = cameraPosition.y + cameraToExclusionIntersectionY;
|
|
float exclusionIntersectionToReceiverY = cameraToReceiver.y - cameraToExclusionIntersectionY;
|
|
|
|
// Calculate fog off of the ray starting from the exclusion distance, instead of starting from the camera
|
|
rayLength = (1.0f - excludeIntersectionTime) * cameraToReceiverLength;
|
|
rayDirectionY = exclusionIntersectionToReceiverY;
|
|
|
|
// Move off the viewer
|
|
float exponent = exponentialHeightFog.FogHeightFalloff * (exclusionIntersectionY - exponentialHeightFog.FogHeight);
|
|
rayOriginTerms = exponentialHeightFog.FogDensity * exp2(-exponent);
|
|
}
|
|
|
|
// Calculate the line integral of the ray from the camera to the receiver position through the fog density function
|
|
float falloff = max(-127.0f, exponentialHeightFog.FogHeightFalloff * rayDirectionY);
|
|
float lineIntegral = (1.0f - exp2(-falloff)) / falloff;
|
|
float lineIntegralTaylor = log(2.0) - (0.5 * Pow2(log(2.0))) * falloff;
|
|
float exponentialHeightLineIntegralCalc = rayOriginTerms * (abs(falloff) > 0.01f ? lineIntegral : lineIntegralTaylor);
|
|
float exponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * rayLength;
|
|
|
|
// Calculate the amount of light that made it through the fog using the transmission equation
|
|
half expFogFactor = max(saturate(exp2(-exponentialHeightLineIntegral)), exponentialHeightFog.FogMinOpacity);
|
|
|
|
// Calculate the directional light inscattering
|
|
half3 inscatteringColor = exponentialHeightFog.FogInscatteringColor;
|
|
half3 directionalInscattering = 0;
|
|
BRANCH
|
|
if (exponentialHeightFog.ApplyDirectionalInscattering > 0)
|
|
{
|
|
// Setup a cosine lobe around the light direction to approximate inscattering from the directional light off of the ambient haze
|
|
half3 directionalLightInscattering = exponentialHeightFog.DirectionalInscatteringColor * pow(saturate(dot(cameraToReceiverNormalized, exponentialHeightFog.InscatteringLightDirection)), exponentialHeightFog.DirectionalInscatteringExponent);
|
|
|
|
// Calculate the line integral of the eye ray through the haze, using a special starting distance to limit the inscattering to the distance
|
|
float dirExponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * max(rayLength - exponentialHeightFog.DirectionalInscatteringStartDistance, 0.0f);
|
|
|
|
// Calculate the amount of light that made it through the fog using the transmission equation
|
|
half directionalInscatteringFogFactor = saturate(exp2(-dirExponentialHeightLineIntegral));
|
|
|
|
// Final inscattering from the light
|
|
directionalInscattering = directionalLightInscattering * (1 - directionalInscatteringFogFactor);
|
|
}
|
|
|
|
// Disable fog after a certain distance
|
|
FLATTEN
|
|
if (exponentialHeightFog.FogCutoffDistance > 0 && cameraToReceiverLength > exponentialHeightFog.FogCutoffDistance)
|
|
{
|
|
expFogFactor = 1;
|
|
directionalInscattering = 0;
|
|
}
|
|
|
|
return half4((inscatteringColor) * (1 - expFogFactor) + directionalInscattering, expFogFactor);
|
|
}
|
|
|
|
#endif
|