365 lines
12 KiB
GLSL
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);
|
|
}
|