diff --git a/Content/Shaders/AtmospherePreCompute.flax b/Content/Shaders/AtmospherePreCompute.flax index 099575745..2a778c8df 100644 --- a/Content/Shaders/AtmospherePreCompute.flax +++ b/Content/Shaders/AtmospherePreCompute.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7bb50886a9a240de754b0f81d6119338846a082f7e452f32376de958917d2e14 -size 13312 +oid sha256:17e3ad3624b4662280ef28e989768a8e6f139568f8ec9fd20f0e0b62fd3713eb +size 11720 diff --git a/Source/Engine/Renderer/AtmospherePreCompute.cpp b/Source/Engine/Renderer/AtmospherePreCompute.cpp index 90eb0a0d1..833e3d0cf 100644 --- a/Source/Engine/Renderer/AtmospherePreCompute.cpp +++ b/Source/Engine/Renderer/AtmospherePreCompute.cpp @@ -61,11 +61,11 @@ protected: PACK_STRUCT(struct Data { - float FirstOrder; + float First; float AtmosphereR; int AtmosphereLayer; float Dummy0; - Vector4 DhdH; + Vector4 dhdh; }); namespace AtmospherePreComputeImpl @@ -367,17 +367,17 @@ void AtmospherePreComputeService::Dispose() release(); } -void GetLayerValue(int32 layer, float& atmosphereR, Vector4& DhdH) +void GetLayerValue(int32 layer, float& atmosphereR, Vector4& dhdh) { float r = layer / Math::Max(InscatterAltitudeSampleNum - 1.0f, 1.0f); r = r * r; r = Math::Sqrt(RadiusGround * RadiusGround + r * (RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround)) + (layer == 0 ? 0.01f : (layer == InscatterAltitudeSampleNum - 1 ? -0.001f : 0.0f)); - float dMin = RadiusAtmosphere - r; - float dMax = Math::Sqrt(r * r - RadiusGround * RadiusGround) + Math::Sqrt(RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround); - float dMinP = r - RadiusGround; - float dMaxP = Math::Sqrt(r * r - RadiusGround * RadiusGround); + const float dMin = RadiusAtmosphere - r; + const float dMax = Math::Sqrt(r * r - RadiusGround * RadiusGround) + Math::Sqrt(RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround); + const float dMinP = r - RadiusGround; + const float dMaxP = Math::Sqrt(r * r - RadiusGround * RadiusGround); atmosphereR = r; - DhdH = Vector4(dMin, dMax, dMinP, dMaxP); + dhdh = Vector4(dMin, dMax, dMinP, dMaxP); } void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context) @@ -390,8 +390,8 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context) } ASSERT(_isUpdatePending && _updateFrameNumber == 0); - auto shader = _shader->GetShader(); - auto cb = shader->GetCB(0); + const auto shader = _shader->GetShader(); + const auto cb = shader->GetCB(0); Data data; // Compute transmittance texture T (line 1 in algorithm 4.1) @@ -415,7 +415,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context) context->SetState(_psInscatter1_A); for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) { - GetLayerValue(layer, data.AtmosphereR, data.DhdH); + GetLayerValue(layer, data.AtmosphereR, data.dhdh); data.AtmosphereLayer = layer; context->UpdateCB(cb, &data); context->BindCB(0, cb); @@ -426,7 +426,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context) context->SetState(_psInscatter1_B); for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) { - GetLayerValue(layer, data.AtmosphereR, data.DhdH); + GetLayerValue(layer, data.AtmosphereR, data.dhdh); data.AtmosphereLayer = layer; context->UpdateCB(cb, &data); context->BindCB(0, cb); @@ -439,7 +439,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context) //// old way to render Inscatter1 to DeltaSR and DeltaSM at once but didn't work well :/ (no time to find out why) //for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) //{ - // GetLayerValue(layer, data.AtmosphereR, data.DhdH); + // GetLayerValue(layer, data.AtmosphereR, data.dhdh); // data.AtmosphereLayer = layer; // cb->SetData(&data); // context->Bind(0, cb); @@ -472,7 +472,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context) context->BindSR(5, AtmosphereDeltaSM->ViewVolume()); for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) { - GetLayerValue(layer, data.AtmosphereR, data.DhdH); + GetLayerValue(layer, data.AtmosphereR, data.dhdh); data.AtmosphereLayer = layer; context->UpdateCB(cb, &data); context->BindCB(0, cb); @@ -489,14 +489,14 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context) context->UnBindSR(6); context->SetViewportAndScissors((float)InscatterWidth, (float)InscatterHeight); context->SetState(_psInscatterS); - data.FirstOrder = order == 2 ? 1.0f : 0.0f; + data.First = order == 2 ? 1.0f : 0.0f; context->BindSR(0, AtmosphereTransmittance); context->BindSR(3, AtmosphereDeltaE); context->BindSR(4, AtmosphereDeltaSR->ViewVolume()); context->BindSR(5, AtmosphereDeltaSM->ViewVolume()); for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) { - GetLayerValue(layer, data.AtmosphereR, data.DhdH); + GetLayerValue(layer, data.AtmosphereR, data.dhdh); data.AtmosphereLayer = layer; context->UpdateCB(cb, &data); context->BindCB(0, cb); @@ -523,7 +523,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context) context->BindSR(6, AtmosphereDeltaJ->ViewVolume()); for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) { - GetLayerValue(layer, data.AtmosphereR, data.DhdH); + GetLayerValue(layer, data.AtmosphereR, data.dhdh); data.AtmosphereLayer = layer; context->UpdateCB(cb, &data); context->BindCB(0, cb); @@ -545,7 +545,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context) context->BindSR(4, AtmosphereDeltaSR->ViewVolume()); for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) { - GetLayerValue(layer, data.AtmosphereR, data.DhdH); + GetLayerValue(layer, data.AtmosphereR, data.dhdh); data.AtmosphereLayer = layer; context->UpdateCB(cb, &data); context->BindCB(0, cb); diff --git a/Source/Shaders/Atmosphere.hlsl b/Source/Shaders/Atmosphere.hlsl index 9ac9f2a63..38029f81d 100644 --- a/Source/Shaders/Atmosphere.hlsl +++ b/Source/Shaders/Atmosphere.hlsl @@ -57,9 +57,8 @@ const static int InscatterNuNum = 8; const static int AtmosphericFogInscatterAltitudeSampleNum = 4; // Configuration -#define TRANSMITTANCE_NON_LINEAR 1 -#define INSCATTER_NON_LINEAR 1 -#define ATMOSPHERIC_TEXTURE_SAMPLE_FIX 1 +#define TRANSMITTANCE_NON_LINEAR 1 +#define INSCATTER_NON_LINEAR 1 #ifndef ATMOSPHERIC_NO_SUN_DISK #define ATMOSPHERIC_NO_SUN_DISK 0 diff --git a/Source/Shaders/AtmosphereFog.hlsl b/Source/Shaders/AtmosphereFog.hlsl index 43693e26d..d4aab461a 100644 --- a/Source/Shaders/AtmosphereFog.hlsl +++ b/Source/Shaders/AtmosphereFog.hlsl @@ -24,39 +24,38 @@ static const float HeightOffset = 0.01f; -/** inscattered light along ray x+tv, when sun in direction s (=S[L]-T(x,x0)S[L]|x0) */ -float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S, float radius, float Mu, out float3 attenuation, bool isSceneGeometry) +// inscattered light along ray x+tv, when sun in direction s (=S[L]-T(x,x0)S[L]|x0) +float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S, float radius, float Mu, out float3 attenuation, bool isSceneGeometry) { - float3 result = float3(0.f, 0.f, 0.f); // X in space and ray looking in space, intialize - attenuation = float3(1.f, 1.f, 1.f); + float3 result = float3(0.0f, 0.0f, 0.0f); + attenuation = float3(1.0f, 1.0f, 1.0f); - float D = -radius * Mu - sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere); - if (D > 0.0) - { - // if X in space and ray intersects atmosphere - // move X to nearest intersection of ray with top atmosphere boundary - X += D * V; - T -= D; - Mu = (radius * Mu + D) / RadiusAtmosphere; - radius = RadiusAtmosphere; - } - - float Epsilon = 0.005f;//maybe 0.004? - - if (radius < RadiusGround + HeightOffset + Epsilon) + float d = -radius * Mu - sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere); + if (d > 0.0f) { - float Diff = (RadiusGround + HeightOffset + Epsilon) - radius; - X -= Diff * V; - T -= Diff; - radius = RadiusGround + HeightOffset + Epsilon; + // if X in space and ray intersects atmosphere + // move X to nearest intersection of ray with top atmosphere boundary + X += d * V; + T -= d; + Mu = (radius * Mu + d) / RadiusAtmosphere; + radius = RadiusAtmosphere; + } + + float epsilon = 0.005f; + + if (radius < RadiusGround + HeightOffset + epsilon) + { + float diff = (RadiusGround + HeightOffset + epsilon) - radius; + X -= diff * V; + T -= diff; + radius = RadiusGround + HeightOffset + epsilon; Mu = dot(X, V) / radius; } - if (radius <= RadiusAtmosphere && fogDepth > 0.f) - { + if (radius <= RadiusAtmosphere && fogDepth > 0.0f) + { float3 X0 = X + T * V; float R0 = length(X0); - // if ray intersects atmosphere float Nu = dot(V, S); float MuS = dot(X, S) / radius; @@ -64,11 +63,11 @@ float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S, if (isSceneGeometry) { - Mu = max(Mu, MuHorizon + Epsilon + 0.15); + Mu = max(Mu, MuHorizon + epsilon + 0.15f); } else - { - Mu = max(Mu, MuHorizon + Epsilon); + { + Mu = max(Mu, MuHorizon + epsilon); } float MuOriginal = Mu; @@ -81,9 +80,8 @@ float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S, { V.z = max(V.z, 0.15); V = normalize(V); - float3 X1 = X + T * V; - float R1 = length(X1); - Mu = dot(X1, V) / R1; + float3 x1 = X + T * V; + Mu = dot(x1, V) / length(x1); } } @@ -91,212 +89,185 @@ float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S, float phaseM = PhaseFunctionM(Nu); float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu), 0.0); - if (T > 0.0) + if (T > 0.0) { -#if ATMOSPHERIC_TEXTURE_SAMPLE_FIX - // Avoids imprecision problems in transmittance computations based on textures attenuation = AnalyticTransmittance(radius, Mu, T); -#else - attenuation = TransmittanceWithDistance(radius, Mu, V, X0); -#endif - float Mu0 = dot(X0, V) / R0; float MuS0 = dot(X0, S) / R0; - if (isSceneGeometry) { R0 = max(R0, radius); } - + if (R0 > RadiusGround + HeightOffset) { if (blendRatio < 1.0) { inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu), 0.0); -#if ATMOSPHERIC_TEXTURE_SAMPLE_FIX - // avoids imprecision problems near horizon by interpolating between two points above and below horizon - if (!isSceneGeometry ) + if (!isSceneGeometry) { - if (abs(Mu - MuHorizon) < Epsilon) + if (abs(Mu - MuHorizon) < epsilon) { - float Alpha = ((Mu - MuHorizon) + Epsilon) * 0.5f / Epsilon; - - Mu = MuHorizon - Epsilon; + Mu = MuHorizon - epsilon; R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu); Mu0 = (radius * Mu + T) / R0; - Mu0 = max(MuHorizon + Epsilon, Mu0); - float4 Inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu); - float4 Inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu); - float4 InscatterA = max(Inscatter0 - attenuation.rgbr * Inscatter1, 0.0); + Mu0 = max(MuHorizon + epsilon, Mu0); + float4 inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu); + float4 inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu); + float4 inscatterA = max(inscatter0 - attenuation.rgbr * inscatter1, 0.0); - Mu = MuHorizon + Epsilon; + Mu = MuHorizon + epsilon; R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu); Mu0 = (radius * Mu + T) / R0; - Mu0 = max(MuHorizon + Epsilon, Mu0); - Inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu); - Inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu); - float4 InscatterB = max(Inscatter0 - attenuation.rgbr * Inscatter1, 0.0); + Mu0 = max(MuHorizon + epsilon, Mu0); + inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu); + inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu); + float4 inscatterB = max(inscatter1 - attenuation.rgbr * inscatter1, 0.0); - inscatter = lerp(InscatterA, InscatterB, Alpha); + float alpha = ((Mu - MuHorizon) + epsilon) * 0.5f / epsilon; + inscatter = lerp(inscatterA, inscatterB, alpha); } } else if (blendRatio > 0.0) { - inscatter = lerp(inscatter, (1.0 - attenuation.rgbr) * max(Texture4DSample(AtmosphereInscatterTexture, radius, MuOriginal, MuS, Nu), 0.0), blendRatio); + inscatter = lerp(inscatter, (1.0 - attenuation.rgbr) * max(Texture4DSample(AtmosphereInscatterTexture, radius, MuOriginal, MuS, Nu), 0.0), blendRatio); } -#endif } else { - inscatter = (1.0 - attenuation.rgbr) * inscatter; + inscatter = (1.0 - attenuation.rgbr) * inscatter; } } } -#if ATMOSPHERIC_TEXTURE_SAMPLE_FIX - // Avoids imprecision problems in Mie scattering when sun is below horizon - inscatter.w *= smoothstep(0.00, 0.02, MuS); -#endif - result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0); - } - + + inscatter.w *= smoothstep(0.00, 0.02, MuS); + result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0); + } + return result; } -// Ground radiance at end of ray x+tv, when sun in direction s attenuated bewteen ground and viewer (=R[L0]+R[L*]) +// Ground radiance at end of ray x+tv, when sun in direction s attenuated between ground and viewer (=R[L0]+R[L*]) float3 GetGroundColor(float4 sceneColor, float3 X, float T, float3 V, float3 S, float radius, float3 attenuation, bool isSceneGeometry) { - float3 result = float3(0.f, 0.f, 0.f); // ray looking at the sky (for intial value) - if (T > 0.0) - { + float3 result = float3(0.0f, 0.0f, 0.0f); + if (T > 0.0f) + { // if ray hits ground surface - // ground Reflectance at end of ray, X0 - float3 X0 = X + T * V; - float R0 = length(X0); - float3 N = X0 / R0; - N = X0 / R0; + // ground Reflectance at end of ray, X0 + float3 X0 = X + T * V; + float R0 = length(X0); + float3 N = X0 / R0; sceneColor.xyz = saturate(sceneColor.xyz + 0.05); - float4 reflectance = sceneColor * float4(0.2, 0.2, 0.2, 1.0); + float4 reflectance = sceneColor * float4(0.2, 0.2, 0.2, 1.0); - // direct sun light (radiance) reaching X0 - float MuS = dot(N, S); - float3 SunLight = isSceneGeometry ? float3(0.f, 0.f, 0.f) : TransmittanceWithShadow(R0, MuS); + // direct sun light (radiance) reaching X0 + float MuS = dot(N, S); + float3 sunLight = isSceneGeometry ? float3(0.f, 0.f, 0.f) : TransmittanceWithShadow(R0, MuS); - // precomputed sky light (irradiance) (=E[L*]) at X0 - float3 GroundSkyLight = Irradiance(AtmosphereIrradianceTexture, R0, MuS); + // precomputed sky light (irradiance) (=E[L*]) at X0 + float3 groundSkyLight = Irradiance(AtmosphereIrradianceTexture, R0, MuS); - // light reflected at X0 (=(R[L0]+R[L*])/T(X,X0)) - float3 groundColor = (reflectance.rgb * (max(MuS, 0.0) * SunLight + GroundSkyLight)) / PI; + // light reflected at X0 (=(R[L0]+R[L*])/T(X,X0)) + float3 groundColor = reflectance.rgb * ((max(MuS, 0.0) * sunLight + groundSkyLight) / PI); - // water specular color due to SunLight - if (!isSceneGeometry && reflectance.w > 0.0) + // water specular color due to SunLight + if (!isSceneGeometry && reflectance.w > 0.0) { - float3 H = normalize(S - V); - float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-V, H), 5.0); - float waterBrdf = fresnel * pow(max(dot(H, N), 0.0), 150.0); - groundColor += reflectance.w * max(waterBrdf, 0.0) * SunLight; - } + float3 H = normalize(S - V); + float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-V, H), 5.0); + float waterBrdf = fresnel * pow(max(dot(H, N), 0.0), 150.0); + groundColor += reflectance.w * max(waterBrdf, 0.0) * sunLight; + } result = attenuation * groundColor; //=R[L0]+R[L*] - } - return result; + } + return result; } // Direct sun light for ray x+tv, when sun in direction s (=L0) -float3 GetSunColor(AtmosphericFogData atmosphericFog, float3 X, float T, float3 V, float3 S, float radius, float Mu) +float3 GetSunColor(AtmosphericFogData atmosphericFog, float3 X, float T, float3 V, float3 S, float radius, float Mu) { if (T > 0.0) - { return float3(0.0f, 0.0f, 0.0f); - } - else - { - float3 transmittance = radius <= RadiusAtmosphere ? TransmittanceWithShadow(radius, Mu) : float3(1.0, 1.0, 1.0); // T(X,xo) - float sunIntensity = step(cos(PI * atmosphericFog.AtmosphericFogSunDiscScale / 180.0), dot(V, S)); // Lsun - return transmittance * sunIntensity; // Eq (9) - } + + float3 transmittance = radius <= RadiusAtmosphere ? TransmittanceWithShadow(radius, Mu) : float3(1.0, 1.0, 1.0); // T(X,xo) + float sunIntensity = step(cos(PI * atmosphericFog.AtmosphericFogSunDiscScale / 180.0), dot(V, S)); // Lsun + return transmittance * sunIntensity; // Eq (9) } float3 inscatter(inout float3 x, inout float t, float3 v, float3 s, out float r, out float mu, out float3 attenuation) { - float3 result = 0; - r = length(x); - mu = dot(x, v) / r; - float d = -r * mu - sqrt(r * r * (mu * mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere); + float3 result = 0; + r = length(x); + mu = dot(x, v) / r; + float d = -r * mu - sqrt(r * r * (mu * mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere); // if x in space and ray intersects atmosphere - if (d > 0.0) + if (d > 0.0) { - // move x to nearest intersection of ray with top atmosphere boundary - x += d * v; - t -= d; - mu = (r * mu + d) / RadiusAtmosphere; - r = RadiusAtmosphere; - } + // move x to nearest intersection of ray with top atmosphere boundary + x += d * v; + t -= d; + mu = (r * mu + d) / RadiusAtmosphere; + r = RadiusAtmosphere; + } + + float epsilon = 0.0045f; // if ray intersects atmosphere - if (r <= RadiusAtmosphere) + if (r <= RadiusAtmosphere) { - float nu = dot(v, s); - float muS = dot(x, s) / r; - float phaseR = PhaseFunctionR(nu); - float phaseM = PhaseFunctionM(nu); - float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu), 0.0); + float nu = dot(v, s); + float muS = dot(x, s) / r; + float phaseR = PhaseFunctionR(nu); + float phaseM = PhaseFunctionM(nu); + float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu), 0.0); - if (t > 0.0) + if (t > 0.0) { - float3 x0 = x + t * v; - float r0 = length(x0); - float rMu0 = dot(x0, v); - float mu0 = rMu0 / r0; - float muS0 = dot(x0, s) / r0; -#ifdef ATMOSPHERIC_TEXTURE_SAMPLE_FIX - // avoids imprecision problems in transmittance computations based on textures + float3 x0 = x + t * v; + float r0 = length(x0); + float rMu0 = dot(x0, v); + float mu0 = rMu0 / r0; + float muS0 = dot(x0, s) / r0; attenuation = AnalyticTransmittance(r, mu, t); -#else - attenuation = TransmittanceWithDistance(r, mu, v, x0); -#endif - if (r0 > RadiusGround + 0.01) + if (r0 > RadiusGround + 0.01) { - // computes S[L]-T(x,x0)S[L]|x0 - inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu), 0.0); -#ifdef ATMOSPHERIC_TEXTURE_SAMPLE_FIX - // avoids imprecision problems near horizon by interpolating between two points above and below horizon - const float EPS = 0.004; + // computes S[L]-T(x,x0)S[L]|x0 + inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu), 0.0); float muHoriz = -sqrt(1.0 - (RadiusGround / r) * (RadiusGround / r)); - if (abs(mu - muHoriz) < EPS) + if (abs(mu - muHoriz) < epsilon) { - float a = ((mu - muHoriz) + EPS) / (2.0 * EPS); - mu = muHoriz - EPS; + mu = muHoriz - epsilon; r0 = sqrt(r * r + t * t + 2.0 * r * t * mu); mu0 = (r * mu + t) / r0; float4 inScatter0 = Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu); float4 inScatter1 = Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu); float4 inScatterA = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0); - mu = muHoriz + EPS; + mu = muHoriz + epsilon; r0 = sqrt(r * r + t * t + 2.0 * r * t * mu); mu0 = (r * mu + t) / r0; inScatter0 = Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu); inScatter1 = Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu); float4 inScatterB = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0); - - inscatter = lerp(inScatterA, inScatterB, a); + + float alpha = ((mu - muHoriz) + epsilon) / (2.0 * epsilon); + inscatter = lerp(inScatterA, inScatterB, alpha); } -#endif - } - } -#ifdef ATMOSPHERIC_TEXTURE_SAMPLE_FIX - // avoids imprecision problems in Mie scattering when sun is below horizon - inscatter.w *= smoothstep(0.00, 0.02, muS); -#endif - result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0); - } + } + } - return result; + inscatter.w *= smoothstep(0.00, 0.02, muS); + result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0); + } + + return result; } static const float EPSILON_ATMOSPHERE = 0.002f; @@ -308,19 +279,19 @@ static const float EPSILON_INSCATTER = 0.004f; // output - maxPathLength: distance traversed within atmosphere // output - return value: intersection occurred true/false bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset, out float maxPathLength) -{ +{ offset = 0.0f; maxPathLength = 0.0f; - + // vector from ray origin to center of the sphere float3 l = -viewPosition; - float l2 = dot(l,l); - float s = dot(l,d); - + float l2 = dot(l, l); + float s = dot(l, d); + // adjust top atmosphere boundary by small epsilon to prevent artifacts float r = Rt - EPSILON_ATMOSPHERE; - float r2 = r*r; - if(l2 <= r2) + float r2 = r * r; + if (l2 <= r2) { // ray origin inside sphere, hit is ensured float m2 = l2 - (s * s); @@ -328,11 +299,11 @@ bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset, maxPathLength = s + q; return true; } - else if(s >= 0) + else if (s >= 0) { // ray starts outside in front of sphere, hit is possible float m2 = l2 - (s * s); - if(m2 <= r2) + if (m2 <= r2) { // ray hits atmosphere definitely float q = sqrt(r2 - m2); @@ -341,7 +312,7 @@ bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset, return true; } } - + return false; } @@ -358,7 +329,7 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi if (intersectAtmosphere(viewPosition, viewDir, offset, maxPathLength)) { - return float3(offset / 10,0,0); + return float3(offset / 10, 0, 0); float pathLength = distance(viewPosition, surfacePos); //return pathLength.xxx; @@ -367,10 +338,11 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi if (pathLength > offset) { //return float3(1,0,0); - + // offsetting camera float3 startPos = viewPosition + offset * viewDir; - float startPosHeight = length(startPos); pathLength -= offset; + float startPosHeight = length(startPos); + pathLength -= offset; // starting position of path is now ensured to be inside atmosphere // was either originally there or has been moved to top boundary @@ -457,9 +429,9 @@ float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float viewPosition.y = (viewPosition.y - atmosphericFog.AtmosphericFogGroundOffset) * atmosphericFog.AtmosphericFogAltitudeScale; //viewPosition *= atmosphericFog.AtmosphericFogDistanceScale; //viewPosition *= scale; - + //viewPosition *= scale; - + //if(length(worldPosition) > Rg) // return float4(0, 0,0 ,0); @@ -480,27 +452,27 @@ float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float return float4(0, 0, 0, 1); #endif - + #if 1 // TODO: scale viewPosition from cm to km - + float scale = 0.0001f * atmosphericFog.AtmosphericFogDistanceScale; viewPosition.y = (viewPosition.y - atmosphericFog.AtmosphericFogGroundOffset) * atmosphericFog.AtmosphericFogAltitudeScale; //viewPosition *= atmosphericFog.AtmosphericFogDistanceScale; viewPosition *= scale; //viewPosition.xz *= 0.00001f; - + //viewPosition *= scale; viewPosition.y += RadiusGround + HeightOffset; - + //viewPosition.y -= atmosphericFog.AtmosphericFogGroundOffset; //worldPosition float Radius = length(viewPosition); float3 V = normalize(viewVector); float Mu = dot(viewPosition, V) / Radius; float T = -Radius * Mu - sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround); - + //-Radius * Mu > sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround) /* float3 g = viewPosition - float3(0.0, 0.0, RadiusGround + 10.0); diff --git a/Source/Shaders/AtmospherePreCompute.shader b/Source/Shaders/AtmospherePreCompute.shader index e5013cbd3..372b97306 100644 --- a/Source/Shaders/AtmospherePreCompute.shader +++ b/Source/Shaders/AtmospherePreCompute.shader @@ -3,6 +3,23 @@ #include "./Flax/Common.hlsl" #include "./Flax/Atmosphere.hlsl" +// Provides functions for atmospheric scattering and aerial perspective. +// +// Explanations: +// Scale Height = the altitude (height above ground) at which the average +// atmospheric density is found. +// Optical Depth = also called optical length, airmass, etc. +// +// References: +// [GPUGems2] GPU Gems 2: Accurate Atmospheric Scattering by Sean O'Neil. +// [GPUPro3] An Approximation to the Chapman Grazing-Incidence Function for +// Atmospheric Scattering, GPU Pro3, pp. 105. +// Papers bei Bruneton, Nishita, etc. +// +// This code contains embedded portions of free sample source code from +// http://www-evasion.imag.fr/Membres/Eric.Bruneton/PrecomputedAtmosphericScattering2.zip, Author: Eric Bruneton, +// 08/16/2011, Copyright (c) 2008 INRIA, All Rights Reserved, which have been altered from their original version. + const static int TransmittanceIntegralSamples = 500; const static int InscatterIntegralSamples = 50; const static int IrradianceIntegralSamples = 32; @@ -14,11 +31,11 @@ const static float InscatterDeltaPhi = PI / float(InscatterSphericalIntegralSamp const static float InscatterDeltaTheta = PI / float(InscatterSphericalIntegralSamples); META_CB_BEGIN(0, Data) -float FirstOrder; +float First; float AtmosphereR; int AtmosphereLayer; float Dummy0; -float4 DhdH; +float4 dhdh; META_CB_END Texture2D AtmosphereDeltaETexture : register(t3); @@ -26,117 +43,72 @@ Texture3D AtmosphereDeltaSRTexture : register(t4); Texture3D AtmosphereDeltaSMTexture : register(t5); Texture3D AtmosphereDeltaJTexture : register(t6); -struct AtmosphereGSOutput +float GetOpticalDepth(float h, float radius, float mu) { - 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 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 Ti = Limit(radius, Mu) / float(TransmittanceIntegralSamples); - float Xi = 0.0; - float Yi = exp(-(radius - RadiusGround) / H); - + float result = 0.0f; + float ti = Limit(radius, mu) / float(TransmittanceIntegralSamples); + float xi = 0.0f; + float yi = exp(-(radius - RadiusGround) / h); LOOP for (int i = 1; i <= TransmittanceIntegralSamples; i++) { - float Xj = float(i) * Ti; - float Yj = exp(-(sqrt(radius * radius + Xj * Xj + 2.0 * Xj * radius * Mu) - RadiusGround) / H); - result += (Yi + Yj) / 2.0 * Ti; - Xi = Xj; - Yi = Yj; + float xj = float(i) * ti; + float yj = exp(-(sqrt(radius * radius + xj * xj + 2.0f * xj * radius * mu) - RadiusGround) / h); + result += (yi + yj) * 0.5f * ti; + xi = xj; + yi = yj; } - - return Mu < -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius)) ? 1e9 : result; + return mu < -sqrt(1.0f - (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) + float radius, mus; + GetTransmittanceRMuS(input.TexCoord, radius, mus); + float3 depth = BetaRayleighScattering * GetOpticalDepth(HeightScaleRayleigh, radius, mus) + BetaMieExtinction * GetOpticalDepth(HeightScaleMie, radius, mus); + return float4(exp(-depth), 0.0f); } 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); + float radius, mus; + GetIrradianceRMuS(input.TexCoord, radius, mus); + return float4(Transmittance(radius, mus) * max(mus, 0.0f), 0.0f); } 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) + float radius, mus; + GetIrradianceRMuS(input.TexCoord, radius, mus); + float3 s = float3(sqrt(max(1.0f - mus * mus, 0.0f)), 0.0f, mus); + float3 result = float3(0, 0, 0); for (int iPhi = 0; iPhi < 4 * IrradianceIntegralSamplesHalf; iPhi++) { - float phi = (float(iPhi) + 0.5) * IrradianceDeltaPhi; + float phi = (float(iPhi) + 0.5f) * 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); + float theta = (float(iTheta) + 0.5f) * 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) + if (First == 1.0f) { - // 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; + float pr1 = PhaseFunctionR(nu); + float pm1 = PhaseFunctionM(nu); + float3 ray1 = Texture4DSample(AtmosphereDeltaSRTexture, radius, w.z, mus, nu).xyz; + float3 mie1 = Texture4DSample(AtmosphereDeltaSMTexture, radius, w.z, mus, nu).xyz; + result += (ray1 * pr1 + mie1 * pm1) * w.z * dw; } else { - result += Texture4DSample(AtmosphereDeltaSRTexture, radius, W.z, MuS, Nu).rgb * W.z * Dw; + result += Texture4DSample(AtmosphereDeltaSRTexture, radius, w.z, mus, nu).xyz * w.z * dw; } } } - return float4(result, 0.0); } @@ -146,217 +118,185 @@ 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) +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)) ) + ray = float3(0, 0, 0); + mie = float3(0, 0, 0); + float ri = sqrt(radius * radius + t * t + 2.0f * 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; + 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) +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); + ray = float3(0, 0, 0); + mie = float3(0, 0, 0); + float dx = Limit(radius, mu) / float(InscatterIntegralSamples); + float xi = 0.0f; + float3 rayi; + float3 miei; + Integrand(radius, mu, mus, nu, 0.0f, rayi, miei); for (int i = 1; i <= InscatterIntegralSamples; i++) { - float Xj = float(i) * Dx; + 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; + Integrand(radius, mu, mus, nu, xj, Rayj, Miej); + ray += (rayi + Rayj) * 0.5f * dx; + mie += (miei + Miej) * 0.5f * 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); + ray *= BetaRayleighScattering; + mie *= BetaMieScattering; } META_PS(true, FEATURE_LEVEL_ES2) -float4 PS_CopyInscatter1(AtmosphereGSOutput input) : SV_Target0 +float4 PS_Inscatter1_A(Quad_VS2PS input) : SV_Target { - 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); + float3 ray; + float3 mie; + float mu, mus, nu; + GetMuMuSNu(input.TexCoord, AtmosphereR, dhdh, mu, mus, nu); + Inscatter(AtmosphereR, mu, mus, nu, ray, mie); + return float4(ray, 1); } -// For Inscatter S -void Inscatter(float Radius, float Mu, float MuS, float Nu, out float3 RayMie) +META_PS(true, FEATURE_LEVEL_ES2) +float4 PS_CopyInscatter1(Quad_VS2PS input) : SV_Target0 { - 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); + 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.xyz, mie.x); +} - float cThetaMin = -sqrt(1.0 - (RadiusGround / Radius) * (RadiusGround / Radius)); +void Inscatter(float radius, float mu, float mus, float nu, out float3 rayMie) +{ + radius = clamp(radius, RadiusGround, RadiusAtmosphere); + mu = clamp(mu, -1.0f, 1.0f); + mus = clamp(mus, -1.0f, 1.0f); + float variation = sqrt(1.0f - mu * mu) * sqrt(1.0f - mus * mus); + nu = clamp(nu, mus * mu - variation, mus * mu + variation); + float cThetaMin = -sqrt(1.0f - (RadiusGround / radius) * (RadiusGround / radius)); + float3 v = float3(sqrt(1.0f - mu * mu), 0.0f, mu); + float sx = v.x == 0.0f ? 0.0f : (nu - mus * mu) / v.x; + float3 s = float3(sx, sqrt(max(0.0f, 1.0f - sx * sx - mus * mus)), mus); + rayMie = float3(0, 0, 0); - 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 theta = (float(iTheta) + 0.5f) * InscatterDeltaTheta; float cTheta = cos(theta); - - float GReflectance = 0.0; - float DGround = 0.0; - float3 GTransmittance = float3(0.f, 0.f, 0.f); + + float ground = 0.0f; + float3 transmittance = float3(0, 0, 0); + float reflectance = 0.0f; 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); + { + ground = -radius * cTheta - sqrt(radius * radius * (cTheta * cTheta - 1.0f) + RadiusGround * RadiusGround); + transmittance = TransmittanceWithDistance(RadiusGround, -(radius * cTheta + ground) / RadiusGround, ground); + reflectance = AverageGroundRelectance / PI; } 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 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); + float3 normal = (float3(0.0f, 0.0f, radius) + ground * w) / RadiusGround; + float3 irradiance = Irradiance(AtmosphereDeltaETexture, RadiusGround, dot(normal, s)); + float3 rayMie1 = reflectance * irradiance * transmittance; - 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) + if (First == 1.0f) { - // 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 + float pr1 = PhaseFunctionR(nu1); + float pm1 = PhaseFunctionM(nu1); + float3 ray1 = Texture4DSample(AtmosphereDeltaSRTexture, radius, w.z, mus, nu1).xyz; + float3 mie1 = Texture4DSample(AtmosphereDeltaSMTexture, radius, w.z, mus, nu1).xyz; + rayMie1 += ray1 * pr1 + mie1 * pm1; + } + else { - RayMie1 += Texture4DSample(AtmosphereDeltaSRTexture, Radius, W.z, MuS, Nu1).rgb; + rayMie1 += Texture4DSample(AtmosphereDeltaSRTexture, radius, w.z, mus, nu1).xyz; } - // 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; + 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 +float4 PS_InscatterS(Quad_VS2PS 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 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) +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); + 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).xyz * TransmittanceWithDistance(radius, mu, t); } -// InscatterN -float3 Inscatter(float Radius, float Mu, float MuS, float Nu) +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); - + float3 rayMie = float3(0, 0, 0); + float dx = Limit(radius, mu) / float(InscatterIntegralSamples); + float xi = 0.0f; + float3 raymiei = Integrand(radius, mu, mus, nu, 0.0f); 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; + float xj = float(i) * dx; + float3 RayMiej = Integrand(radius, mu, mus, nu, xj); + rayMie += (raymiei + RayMiej) * 0.5f * dx; + xi = xj; + raymiei = RayMiej; } - - return RayMie; + return rayMie; } META_PS(true, FEATURE_LEVEL_ES2) -float4 PS_InscatterN(AtmosphereGSOutput input) : SV_Target0 +float4 PS_InscatterN(Quad_VS2PS input) : SV_Target0 { - float Mu, MuS, Nu; - GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu); - return float4(Inscatter(AtmosphereR, Mu, MuS, Nu), 0); + 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 +float4 PS_CopyInscatterN(Quad_VS2PS 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); + 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.xyz, 0); } META_PS(true, FEATURE_LEVEL_ES2) -float4 PS_Inscatter1_B(AtmosphereGSOutput input) : SV_Target +float4 PS_Inscatter1_B(Quad_VS2PS 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); - return float4(Mie, 1); + float3 ray; + float3 mie; + float mu, mus, nu; + GetMuMuSNu(input.TexCoord, AtmosphereR, dhdh, mu, mus, nu); + Inscatter(AtmosphereR, mu, mus, nu, ray, mie); + return float4(mie, 1); }