Files
FlaxEngine/Source/Shaders/AtmospherePreCompute.shader
2020-12-07 23:40:54 +01:00

365 lines
12 KiB
GLSL

// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "./Flax/Common.hlsl"
#include "./Flax/Atmosphere.hlsl"
const static int TransmittanceIntegralSamples = 500;
const static int InscatterIntegralSamples = 50;
const static int IrradianceIntegralSamples = 32;
const static int IrradianceIntegralSamplesHalf = IrradianceIntegralSamples / 2;
const static int InscatterSphericalIntegralSamples = 16;
const static float IrradianceDeltaPhi = PI / float(IrradianceIntegralSamples);
const static float IrradianceDeltaTheta = PI / float(IrradianceIntegralSamples);
const static float InscatterDeltaPhi = PI / float(InscatterSphericalIntegralSamples);
const static float InscatterDeltaTheta = PI / float(InscatterSphericalIntegralSamples);
META_CB_BEGIN(0, Data)
float FirstOrder;
float AtmosphereR;
int AtmosphereLayer;
float Dummy0;
float4 DhdH;
META_CB_END
Texture2D AtmosphereDeltaETexture : register(t3);
Texture3D AtmosphereDeltaSRTexture : register(t4);
Texture3D AtmosphereDeltaSMTexture : register(t5);
Texture3D AtmosphereDeltaJTexture : register(t6);
struct AtmosphereGSOutput
{
float4 Position : SV_Position;
float2 TexCoord : TEXCOORD0;
//uint LayerIndex : SV_RenderTargetArrayIndex;
};
/*
META_VS(true, FEATURE_LEVEL_ES2)
META_VS_IN_ELEMENT(POSITION, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
Quad_VS2PS VS(float2 Position : POSITION0, float2 TexCoord : TEXCOORD0)
{
Quad_VS2PS output;
output.Position = float4(Position, 0, 1);
output.TexCoord = TexCoord;
return output;
}
META_GS(true, FEATURE_LEVEL_SM4)
[maxvertexcount(3)]
void GS_Atmosphere(triangle Quad_VS2PS input[3], inout TriangleStream<AtmosphereGSOutput> output)
{
AtmosphereGSOutput vertex;
for(int i = 0; i < 3; i++)
{
vertex.Position = input[i].Position;
vertex.TexCoord = input[i].TexCoord;
vertex.LayerIndex = AtmosphereLayer;
output.Append(vertex);
}
}
*/
float OpticalDepth(float H, float radius, float Mu)
{
float result = 0.0;
float Dx = Limit(radius, Mu) / float(TransmittanceIntegralSamples);
float Xi = 0.0;
float Yi = exp(-(radius - RadiusGround) / H);
LOOP
for (int i = 1; i <= TransmittanceIntegralSamples; i++)
{
float Xj = float(i) * Dx;
float Yj = exp(-(sqrt(radius * radius + Xj * Xj + 2.0 * Xj * radius * Mu) - RadiusGround) / H);
result += (Yi + Yj) / 2.0 * Dx;
Xi = Xj;
Yi = Yj;
}
return Mu < -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius)) ? 1e9 : result;
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_Transmittance(Quad_VS2PS input) : SV_Target0
{
float radius, MuS;
GetTransmittanceRMuS(input.TexCoord, radius, MuS);
float3 depth = BetaRayleighScattering * OpticalDepth(HeightScaleRayleigh, radius, MuS) + BetaMieExtinction * OpticalDepth(HeightScaleMie, radius, MuS);
return float4(exp(-depth), 0.0f); // Eq (5)
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_Irradiance1(Quad_VS2PS input) : SV_Target0
{
float radius, MuS;
GetIrradianceRMuS(input.TexCoord, radius, MuS);
return float4(Transmittance(radius, MuS) * max(MuS, 0.0), 0.0);
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_IrradianceN(Quad_VS2PS input) : SV_Target0
{
float radius, MuS;
GetIrradianceRMuS(input.TexCoord, radius, MuS);
float3 S = float3(sqrt(max(1.0 - MuS * MuS, 0.0)), 0.0, MuS);
float3 result = float3(0.0f, 0.0f, 0.0f);
// Integral over 2.PI around x with two nested loops over W directions (theta, phi) -- Eq (15)
for (int iPhi = 0; iPhi < 4 * IrradianceIntegralSamplesHalf; iPhi++)
{
float phi = (float(iPhi) + 0.5) * IrradianceDeltaPhi;
for (int iTheta = 0; iTheta < IrradianceIntegralSamplesHalf; iTheta++)
{
float theta = (float(iTheta) + 0.5) * IrradianceDeltaTheta;
float Dw = IrradianceDeltaTheta * IrradianceDeltaPhi * sin(theta);
float3 W = float3(cos(phi) * sin(theta), sin(phi) * sin(theta), cos(theta));
float Nu = dot(S, W);
if (FirstOrder == 1.0)
{
// First iteration is special because Rayleigh and Mie were stored separately,
// without the phase functions factors; they must be reintroduced here
float Pr1 = PhaseFunctionR(Nu);
float Pm1 = PhaseFunctionM(Nu);
float3 Ray1 = Texture4DSample(AtmosphereDeltaSRTexture, radius, W.z, MuS, Nu).rgb;
float3 Mie1 = Texture4DSample(AtmosphereDeltaSMTexture, radius, W.z, MuS, Nu).rgb;
result += (Ray1 * Pr1 + Mie1 * Pm1) * W.z * Dw;
}
else
{
result += Texture4DSample(AtmosphereDeltaSRTexture, radius, W.z, MuS, Nu).rgb * W.z * Dw;
}
}
}
return float4(result, 0.0);
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_CopyIrradiance1(Quad_VS2PS input) : SV_Target0
{
return AtmosphereDeltaETexture.Sample(SamplerLinearClamp, input.TexCoord);
}
void Integrand(float radius, float Mu, float MuS, float Nu, float T, out float3 Ray, out float3 Mie)
{
Ray = float3(0, 0, 0);
Mie = float3(0, 0, 0);
float Ri = sqrt(radius * radius + T * T + 2.0 * radius * Mu * T);
float MuSi = (Nu * T + MuS * radius) / Ri;
Ri = max(RadiusGround, Ri);
if (MuSi >= -sqrt(1.0 - RadiusGround * RadiusGround / (Ri * Ri)) )
{
float3 Ti = TransmittanceWithDistance(radius, Mu, T) * Transmittance(Ri, MuSi);
Ray = exp(-(Ri - RadiusGround) / HeightScaleRayleigh) * Ti;
Mie = exp(-(Ri - RadiusGround) / HeightScaleMie) * Ti;
}
}
// For Inscatter 1
void Inscatter(float radius, float Mu, float MuS, float Nu, out float3 Ray, out float3 Mie)
{
Ray = float3(0, 0, 0);
Mie = float3(0, 0, 0);
float Dx = Limit(radius, Mu) / float(InscatterIntegralSamples);
float Xi = 0.0;
float3 Rayi;
float3 Miei;
Integrand(radius, Mu, MuS, Nu, 0.0, Rayi, Miei);
for (int i = 1; i <= InscatterIntegralSamples; i++)
{
float Xj = float(i) * Dx;
float3 Rayj;
float3 Miej;
Integrand(radius, Mu, MuS, Nu, Xj, Rayj, Miej);
Ray += (Rayi + Rayj) / 2.0 * Dx;
Mie += (Miei + Miej) / 2.0 * Dx;
Xi = Xj;
Rayi = Rayj;
Miei = Miej;
}
Ray *= BetaRayleighScattering;
Mie *= BetaMieScattering;
}
struct Inscatter1Output
{
float4 DeltaSR : SV_Target0;
float4 DeltaSM : SV_Target1;
};
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_Inscatter1_A(AtmosphereGSOutput input) : SV_Target
{
float3 Ray;
float3 Mie;
float Mu, MuS, Nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
Inscatter(AtmosphereR, Mu, MuS, Nu, Ray, Mie);
// Store separately Rayleigh and Mie contributions, WITHOUT the phase function factor (cf "Angular precision")
return float4(Ray, 1);
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_CopyInscatter1(AtmosphereGSOutput input) : SV_Target0
{
float3 UVW = float3(input.TexCoord, (float(AtmosphereLayer) + 0.5f) / float(AtmosphericFogInscatterAltitudeSampleNum));
float4 Ray = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, UVW);
float4 Mie = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, UVW);
return float4(Ray.rgb, Mie.r);
}
// For Inscatter S
void Inscatter(float Radius, float Mu, float MuS, float Nu, out float3 RayMie)
{
Radius = clamp(Radius, RadiusGround, RadiusAtmosphere);
Mu = clamp(Mu, -1.0, 1.0);
MuS = clamp(MuS, -1.0, 1.0);
float Variation = sqrt(1.0 - Mu * Mu) * sqrt(1.0 - MuS * MuS);
Nu = clamp(Nu, MuS * Mu - Variation, MuS * Mu + Variation);
float cThetaMin = -sqrt(1.0 - (RadiusGround / Radius) * (RadiusGround / Radius));
float3 V = float3(sqrt(1.0 - Mu * Mu), 0.0, Mu);
float Sx = V.x == 0.0 ? 0.0 : (Nu - MuS * Mu) / V.x;
float3 S = float3(Sx, sqrt(max(0.0, 1.0 - Sx * Sx - MuS * MuS)), MuS);
RayMie = float3(0.f, 0.f, 0.f);
// Integral over 4.PI around x with two nested loops over W directions (theta, phi) - Eq (7)
for (int iTheta = 0; iTheta < InscatterSphericalIntegralSamples; iTheta++)
{
float theta = (float(iTheta) + 0.5) * InscatterDeltaTheta;
float cTheta = cos(theta);
float GReflectance = 0.0;
float DGround = 0.0;
float3 GTransmittance = float3(0.f, 0.f, 0.f);
if (cTheta < cThetaMin)
{
// If ground visible in direction W, Compute transparency GTransmittance between x and ground
GReflectance = AverageGroundRelectance / PI;
DGround = -Radius * cTheta - sqrt(Radius * Radius * (cTheta * cTheta - 1.0) + RadiusGround * RadiusGround);
GTransmittance = TransmittanceWithDistance(RadiusGround, -(Radius * cTheta + DGround) / RadiusGround, DGround);
}
for (int iPhi = 0; iPhi < 2 * InscatterSphericalIntegralSamples; iPhi++)
{
float phi = (float(iPhi) + 0.5) * InscatterDeltaPhi;
float Dw = InscatterDeltaTheta * InscatterDeltaPhi * sin(theta);
float3 W = float3(cos(phi) * sin(theta), sin(phi) * sin(theta), cTheta);
float Nu1 = dot(S, W);
float Nu2 = dot(V, W);
float Pr2 = PhaseFunctionR(Nu2);
float Pm2 = PhaseFunctionM(Nu2);
// Compute irradiance received at ground in direction W (if ground visible) =deltaE
float3 GNormal = (float3(0.0, 0.0, Radius) + DGround * W) / RadiusGround;
float3 GIrradiance = Irradiance(AtmosphereDeltaETexture, RadiusGround, dot(GNormal, S));
float3 RayMie1; // light arriving at x from direction W
// First term = light reflected from the ground and attenuated before reaching x, =T.alpha/PI.deltaE
RayMie1 = GReflectance * GIrradiance * GTransmittance;
// Second term = inscattered light, =deltaS
if (FirstOrder == 1.0)
{
// First iteration is special because Rayleigh and Mie were stored separately,
// without the phase functions factors; they must be reintroduced here
float Pr1 = PhaseFunctionR(Nu1);
float Pm1 = PhaseFunctionM(Nu1);
float3 Ray1 = Texture4DSample(AtmosphereDeltaSRTexture, Radius, W.z, MuS, Nu1).rgb;
float3 Mie1 = Texture4DSample(AtmosphereDeltaSMTexture, Radius, W.z, MuS, Nu1).rgb;
RayMie1 += Ray1 * Pr1 + Mie1 * Pm1;
}
else
{
RayMie1 += Texture4DSample(AtmosphereDeltaSRTexture, Radius, W.z, MuS, Nu1).rgb;
}
// Light coming from direction W and scattered in direction V
// = light arriving at x from direction W (RayMie1) * SUM(scattering coefficient * phaseFunction) - Eq (7)
RayMie += RayMie1 * (BetaRayleighScattering * exp(-(Radius - RadiusGround) / HeightScaleRayleigh) * Pr2 + BetaMieScattering * exp(-(Radius - RadiusGround) / HeightScaleMie) * Pm2) * Dw;
}
}
// output RayMie = J[T.alpha/PI.deltaE + deltaS] (line 7 in algorithm 4.1)
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_InscatterS(AtmosphereGSOutput input) : SV_Target0
{
float3 RayMie;
float Mu, MuS, Nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
Inscatter(AtmosphereR, Mu, MuS, Nu, RayMie);
return float4(RayMie, 0);
}
float3 Integrand(float Radius, float Mu, float MuS, float Nu, float T)
{
float Ri = sqrt(Radius * Radius + T * T + 2.0 * Radius * Mu * T);
float Mui = (Radius * Mu + T) / Ri;
float MuSi = (Nu * T + MuS * Radius) / Ri;
return Texture4DSample(AtmosphereDeltaJTexture, Ri, Mui, MuSi, Nu).rgb * TransmittanceWithDistance(Radius, Mu, T);
}
// InscatterN
float3 Inscatter(float Radius, float Mu, float MuS, float Nu)
{
float3 RayMie = float3(0.f, 0.f, 0.f);
float Dx = Limit(Radius, Mu) / float(InscatterIntegralSamples);
float Xi = 0.0;
float3 RayMiei = Integrand(Radius, Mu, MuS, Nu, 0.0);
for (int i = 1; i <= InscatterIntegralSamples; i++)
{
float Xj = float(i) * Dx;
float3 RayMiej = Integrand(Radius, Mu, MuS, Nu, Xj);
RayMie += (RayMiei + RayMiej) / 2.0 * Dx;
Xi = Xj;
RayMiei = RayMiej;
}
return RayMie;
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_InscatterN(AtmosphereGSOutput input) : SV_Target0
{
float Mu, MuS, Nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
return float4(Inscatter(AtmosphereR, Mu, MuS, Nu), 0);
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_CopyInscatterN(AtmosphereGSOutput input) : SV_Target0
{
float Mu, MuS, Nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
float3 UVW = float3(input.TexCoord, (float(AtmosphereLayer) + 0.5f) / float(AtmosphericFogInscatterAltitudeSampleNum));
float4 Ray = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, UVW) / PhaseFunctionR(Nu);
return float4(Ray.rgb, 0);
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_Inscatter1_B(AtmosphereGSOutput input) : SV_Target
{
float3 Ray;
float3 Mie;
float Mu, MuS, Nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
Inscatter(AtmosphereR, Mu, MuS, Nu, Ray, Mie);
// Store separately Rayleigh and Mie contributions, WITHOUT the phase function factor (cf "Angular precision")
return float4(Mie, 1);
}