UNPKG

@takram/three-atmosphere

Version:
1,103 lines (1,016 loc) 68.7 kB
const e = `// Based on: https://github.com/ebruneton/precomputed_atmospheric_scattering/blob/master/atmosphere/functions.glsl /** * Copyright (c) 2017 Eric Bruneton * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * Precomputed Atmospheric Scattering * Copyright (c) 2008 INRIA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ Number ClampCosine(const Number mu) { return clamp(mu, Number(-1.0), Number(1.0)); } Length ClampDistance(const Length d) { return max(d, 0.0 * m); } Length ClampRadius(const AtmosphereParameters atmosphere, const Length r) { return clamp(r, atmosphere.bottom_radius, atmosphere.top_radius); } Length SafeSqrt(const Area a) { return sqrt(max(a, 0.0 * m2)); } Length DistanceToTopAtmosphereBoundary(const AtmosphereParameters atmosphere, const Length r, const Number mu) { assert(r <= atmosphere.top_radius); assert(mu >= -1.0 && mu <= 1.0); Area discriminant = r * r * (mu * mu - 1.0) + atmosphere.top_radius * atmosphere.top_radius; return ClampDistance(-r * mu + SafeSqrt(discriminant)); } Length DistanceToBottomAtmosphereBoundary(const AtmosphereParameters atmosphere, const Length r, const Number mu) { assert(r >= atmosphere.bottom_radius); assert(mu >= -1.0 && mu <= 1.0); Area discriminant = r * r * (mu * mu - 1.0) + atmosphere.bottom_radius * atmosphere.bottom_radius; return ClampDistance(-r * mu - SafeSqrt(discriminant)); } bool RayIntersectsGround(const AtmosphereParameters atmosphere, const Length r, const Number mu) { assert(r >= atmosphere.bottom_radius); assert(mu >= -1.0 && mu <= 1.0); return mu < 0.0 && r * r * (mu * mu - 1.0) + atmosphere.bottom_radius * atmosphere.bottom_radius >= 0.0 * m2; } Number GetTextureCoordFromUnitRange(const Number x, const int texture_size) { return 0.5 / Number(texture_size) + x * (1.0 - 1.0 / Number(texture_size)); } vec2 GetTransmittanceTextureUvFromRMu(const AtmosphereParameters atmosphere, const Length r, const Number mu) { assert(r >= atmosphere.bottom_radius && r <= atmosphere.top_radius); assert(mu >= -1.0 && mu <= 1.0); // Distance to top atmosphere boundary for a horizontal ray at ground level. Length H = sqrt(atmosphere.top_radius * atmosphere.top_radius - atmosphere.bottom_radius * atmosphere.bottom_radius); // Distance to the horizon. Length rho = SafeSqrt(r * r - atmosphere.bottom_radius * atmosphere.bottom_radius); // Distance to the top atmosphere boundary for the ray (r,mu), and its minimum // and maximum values over all mu - obtained for (r,1) and (r,mu_horizon). Length d = DistanceToTopAtmosphereBoundary(atmosphere, r, mu); Length d_min = atmosphere.top_radius - r; Length d_max = rho + H; Number x_mu = (d - d_min) / (d_max - d_min); Number x_r = rho / H; return vec2(GetTextureCoordFromUnitRange(x_mu, TRANSMITTANCE_TEXTURE_WIDTH), GetTextureCoordFromUnitRange(x_r, TRANSMITTANCE_TEXTURE_HEIGHT)); } DimensionlessSpectrum GetTransmittanceToTopAtmosphereBoundary( const AtmosphereParameters atmosphere, const TransmittanceTexture transmittance_texture, const Length r, const Number mu) { assert(r >= atmosphere.bottom_radius && r <= atmosphere.top_radius); vec2 uv = GetTransmittanceTextureUvFromRMu(atmosphere, r, mu); // @shotamatsuda: Added for the precomputation stage in half-float precision. #ifdef TRANSMITTANCE_PRECISION_LOG // Manually interpolate the transmittance instead of the optical depth. const vec2 size = vec2(TRANSMITTANCE_TEXTURE_WIDTH, TRANSMITTANCE_TEXTURE_HEIGHT); const vec3 texel_size = vec3(1.0 / size, 0.0); vec2 coord = (uv * size) - 0.5; vec2 i = (floor(coord) + 0.5) * texel_size.xy; vec2 f = fract(coord); vec4 t1 = exp(-texture(transmittance_texture, i)); vec4 t2 = exp(-texture(transmittance_texture, i + texel_size.xz)); vec4 t3 = exp(-texture(transmittance_texture, i + texel_size.zy)); vec4 t4 = exp(-texture(transmittance_texture, i + texel_size.xy)); return DimensionlessSpectrum(mix(mix(t1, t2, f.x), mix(t3, t4, f.x), f.y)); #else // TRANSMITTANCE_PRECISION_LOG return DimensionlessSpectrum(texture(transmittance_texture, uv)); #endif // TRANSMITTANCE_PRECISION_LOG } DimensionlessSpectrum GetTransmittance( const AtmosphereParameters atmosphere, const TransmittanceTexture transmittance_texture, const Length r, const Number mu, const Length d, const bool ray_r_mu_intersects_ground) { assert(r >= atmosphere.bottom_radius && r <= atmosphere.top_radius); assert(mu >= -1.0 && mu <= 1.0); assert(d >= 0.0 * m); Length r_d = ClampRadius(atmosphere, sqrt(d * d + 2.0 * r * mu * d + r * r)); Number mu_d = ClampCosine((r * mu + d) / r_d); if (ray_r_mu_intersects_ground) { return min( GetTransmittanceToTopAtmosphereBoundary( atmosphere, transmittance_texture, r_d, -mu_d) / GetTransmittanceToTopAtmosphereBoundary( atmosphere, transmittance_texture, r, -mu), DimensionlessSpectrum(1.0)); } else { return min( GetTransmittanceToTopAtmosphereBoundary( atmosphere, transmittance_texture, r, mu) / GetTransmittanceToTopAtmosphereBoundary( atmosphere, transmittance_texture, r_d, mu_d), DimensionlessSpectrum(1.0)); } } DimensionlessSpectrum GetTransmittanceToSun( const AtmosphereParameters atmosphere, const TransmittanceTexture transmittance_texture, const Length r, const Number mu_s) { Number sin_theta_h = atmosphere.bottom_radius / r; Number cos_theta_h = -sqrt(max(1.0 - sin_theta_h * sin_theta_h, 0.0)); return GetTransmittanceToTopAtmosphereBoundary( atmosphere, transmittance_texture, r, mu_s) * smoothstep(-sin_theta_h * atmosphere.sun_angular_radius / rad, sin_theta_h * atmosphere.sun_angular_radius / rad, mu_s - cos_theta_h); } InverseSolidAngle RayleighPhaseFunction(const Number nu) { InverseSolidAngle k = 3.0 / (16.0 * PI * sr); return k * (1.0 + nu * nu); } InverseSolidAngle MiePhaseFunction(const Number g, const Number nu) { InverseSolidAngle k = 3.0 / (8.0 * PI * sr) * (1.0 - g * g) / (2.0 + g * g); return k * (1.0 + nu * nu) / pow(1.0 + g * g - 2.0 * g * nu, 1.5); } vec4 GetScatteringTextureUvwzFromRMuMuSNu(const AtmosphereParameters atmosphere, const Length r, const Number mu, const Number mu_s, const Number nu, const bool ray_r_mu_intersects_ground) { assert(r >= atmosphere.bottom_radius && r <= atmosphere.top_radius); assert(mu >= -1.0 && mu <= 1.0); assert(mu_s >= -1.0 && mu_s <= 1.0); assert(nu >= -1.0 && nu <= 1.0); // Distance to top atmosphere boundary for a horizontal ray at ground level. Length H = sqrt(atmosphere.top_radius * atmosphere.top_radius - atmosphere.bottom_radius * atmosphere.bottom_radius); // Distance to the horizon. Length rho = SafeSqrt(r * r - atmosphere.bottom_radius * atmosphere.bottom_radius); Number u_r = GetTextureCoordFromUnitRange(rho / H, SCATTERING_TEXTURE_R_SIZE); // Discriminant of the quadratic equation for the intersections of the ray // (r,mu) with the ground (see RayIntersectsGround). Length r_mu = r * mu; Area discriminant = r_mu * r_mu - r * r + atmosphere.bottom_radius * atmosphere.bottom_radius; Number u_mu; if (ray_r_mu_intersects_ground) { // Distance to the ground for the ray (r,mu), and its minimum and maximum // values over all mu - obtained for (r,-1) and (r,mu_horizon). Length d = -r_mu - SafeSqrt(discriminant); Length d_min = r - atmosphere.bottom_radius; Length d_max = rho; u_mu = 0.5 - 0.5 * GetTextureCoordFromUnitRange(d_max == d_min ? 0.0 : (d - d_min) / (d_max - d_min), SCATTERING_TEXTURE_MU_SIZE / 2); } else { // Distance to the top atmosphere boundary for the ray (r,mu), and its // minimum and maximum values over all mu - obtained for (r,1) and // (r,mu_horizon). Length d = -r_mu + SafeSqrt(discriminant + H * H); Length d_min = atmosphere.top_radius - r; Length d_max = rho + H; u_mu = 0.5 + 0.5 * GetTextureCoordFromUnitRange( (d - d_min) / (d_max - d_min), SCATTERING_TEXTURE_MU_SIZE / 2); } Length d = DistanceToTopAtmosphereBoundary( atmosphere, atmosphere.bottom_radius, mu_s); Length d_min = atmosphere.top_radius - atmosphere.bottom_radius; Length d_max = H; Number a = (d - d_min) / (d_max - d_min); Length D = DistanceToTopAtmosphereBoundary( atmosphere, atmosphere.bottom_radius, atmosphere.mu_s_min); Number A = (D - d_min) / (d_max - d_min); // An ad-hoc function equal to 0 for mu_s = mu_s_min (because then d = D and // thus a = A), equal to 1 for mu_s = 1 (because then d = d_min and thus // a = 0), and with a large slope around mu_s = 0, to get more texture // samples near the horizon. Number u_mu_s = GetTextureCoordFromUnitRange( max(1.0 - a / A, 0.0) / (1.0 + a), SCATTERING_TEXTURE_MU_S_SIZE); Number u_nu = (nu + 1.0) / 2.0; return vec4(u_nu, u_mu_s, u_mu, u_r); } vec2 GetIrradianceTextureUvFromRMuS(const AtmosphereParameters atmosphere, const Length r, const Number mu_s) { assert(r >= atmosphere.bottom_radius && r <= atmosphere.top_radius); assert(mu_s >= -1.0 && mu_s <= 1.0); Number x_r = (r - atmosphere.bottom_radius) / (atmosphere.top_radius - atmosphere.bottom_radius); Number x_mu_s = mu_s * 0.5 + 0.5; return vec2(GetTextureCoordFromUnitRange(x_mu_s, IRRADIANCE_TEXTURE_WIDTH), GetTextureCoordFromUnitRange(x_r, IRRADIANCE_TEXTURE_HEIGHT)); } IrradianceSpectrum GetIrradiance( const AtmosphereParameters atmosphere, const IrradianceTexture irradiance_texture, const Length r, const Number mu_s) { vec2 uv = GetIrradianceTextureUvFromRMuS(atmosphere, r, mu_s); return IrradianceSpectrum(texture(irradiance_texture, uv)); } `, t = `// Based on: https://github.com/ebruneton/precomputed_atmospheric_scattering/blob/master/atmosphere/definitions.glsl /** * Copyright (c) 2017 Eric Bruneton * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #define assert(x) #define Length float #define Wavelength float #define Angle float #define SolidAngle float #define Power float #define LuminousPower float #define Number float #define InverseLength float #define Area float #define Volume float #define NumberDensity float #define Irradiance float #define Radiance float #define SpectralPower float #define SpectralIrradiance float #define SpectralRadiance float #define SpectralRadianceDensity float #define ScatteringCoefficient float #define InverseSolidAngle float #define LuminousIntensity float #define Luminance float #define Illuminance float // A generic function from Wavelength to some other type. #define AbstractSpectrum vec3 // A function from Wavelength to Number. #define DimensionlessSpectrum vec3 // A function from Wavelength to SpectralPower. #define PowerSpectrum vec3 // A function from Wavelength to SpectralIrradiance. #define IrradianceSpectrum vec3 // A function from Wavelength to SpectralRadiance. #define RadianceSpectrum vec3 // A function from Wavelength to SpectralRadianceDensity. #define RadianceDensitySpectrum vec3 // A function from Wavelength to ScatteringCoefficient. #define ScatteringSpectrum vec3 // A position in 3D (3 length values). #define Position vec3 // A unit direction vector in 3D (3 unit-less values). #define Direction vec3 // A vector of 3 luminance values. #define Luminance3 vec3 // A vector of 3 illuminance values. #define Illuminance3 vec3 #define TransmittanceTexture sampler2D #define AbstractScatteringTexture sampler3D #define ReducedScatteringTexture sampler3D #define ScatteringTexture sampler3D #define ScatteringDensityTexture sampler3D #define IrradianceTexture sampler2D const Length m = 1.0; const Wavelength nm = 1.0; const Angle rad = 1.0; const SolidAngle sr = 1.0; const Power watt = 1.0; const LuminousPower lm = 1.0; #if !defined(PI) const float PI = 3.14159265358979323846; #endif // !defined(PI) const Length km = 1000.0 * m; const Area m2 = m * m; const Volume m3 = m * m * m; const Angle pi = PI * rad; const Angle deg = pi / 180.0; const Irradiance watt_per_square_meter = watt / m2; const Radiance watt_per_square_meter_per_sr = watt / (m2 * sr); const SpectralIrradiance watt_per_square_meter_per_nm = watt / (m2 * nm); const SpectralRadiance watt_per_square_meter_per_sr_per_nm = watt / (m2 * sr * nm); const SpectralRadianceDensity watt_per_cubic_meter_per_sr_per_nm = watt / (m3 * sr * nm); const LuminousIntensity cd = lm / sr; const LuminousIntensity kcd = 1000.0 * cd; const Luminance cd_per_square_meter = cd / m2; const Luminance kcd_per_square_meter = kcd / m2; struct DensityProfileLayer { Length width; Number exp_term; InverseLength exp_scale; InverseLength linear_term; Number constant_term; }; struct DensityProfile { DensityProfileLayer layers[2]; }; // See AtmosphereParameter.ts for further details. struct AtmosphereParameters { IrradianceSpectrum solar_irradiance; Angle sun_angular_radius; Length bottom_radius; Length top_radius; DensityProfile rayleigh_density; ScatteringSpectrum rayleigh_scattering; DensityProfile mie_density; ScatteringSpectrum mie_scattering; ScatteringSpectrum mie_extinction; Number mie_phase_function_g; DensityProfile absorption_density; ScatteringSpectrum absorption_extinction; DimensionlessSpectrum ground_albedo; Number mu_s_min; }; `, n = `// Based on: https://github.com/ebruneton/precomputed_atmospheric_scattering/blob/master/atmosphere/functions.glsl /** * Copyright (c) 2017 Eric Bruneton * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * Precomputed Atmospheric Scattering * Copyright (c) 2008 INRIA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef COMBINED_SCATTERING_TEXTURES vec3 GetExtrapolatedSingleMieScattering( const AtmosphereParameters atmosphere, const vec4 scattering) { // Algebraically this can never be negative, but rounding errors can produce // that effect for sufficiently short view rays. // @shotamatsuda: Avoid division by infinitesimal values. // See https://github.com/takram-design-engineering/three-geospatial/issues/47 if (scattering.r < 1e-5) { return vec3(0.0); } return scattering.rgb * scattering.a / scattering.r * (atmosphere.rayleigh_scattering.r / atmosphere.mie_scattering.r) * (atmosphere.mie_scattering / atmosphere.rayleigh_scattering); } #endif // COMBINED_SCATTERING_TEXTURES IrradianceSpectrum GetCombinedScattering( const AtmosphereParameters atmosphere, const ReducedScatteringTexture scattering_texture, const ReducedScatteringTexture single_mie_scattering_texture, const Length r, const Number mu, const Number mu_s, const Number nu, const bool ray_r_mu_intersects_ground, out IrradianceSpectrum single_mie_scattering) { vec4 uvwz = GetScatteringTextureUvwzFromRMuMuSNu( atmosphere, r, mu, mu_s, nu, ray_r_mu_intersects_ground); Number tex_coord_x = uvwz.x * Number(SCATTERING_TEXTURE_NU_SIZE - 1); Number tex_x = floor(tex_coord_x); Number lerp = tex_coord_x - tex_x; vec3 uvw0 = vec3((tex_x + uvwz.y) / Number(SCATTERING_TEXTURE_NU_SIZE), uvwz.z, uvwz.w); vec3 uvw1 = vec3((tex_x + 1.0 + uvwz.y) / Number(SCATTERING_TEXTURE_NU_SIZE), uvwz.z, uvwz.w); #ifdef COMBINED_SCATTERING_TEXTURES vec4 combined_scattering = texture(scattering_texture, uvw0) * (1.0 - lerp) + texture(scattering_texture, uvw1) * lerp; IrradianceSpectrum scattering = IrradianceSpectrum(combined_scattering); single_mie_scattering = GetExtrapolatedSingleMieScattering(atmosphere, combined_scattering); #else // COMBINED_SCATTERING_TEXTURES IrradianceSpectrum scattering = IrradianceSpectrum( texture(scattering_texture, uvw0) * (1.0 - lerp) + texture(scattering_texture, uvw1) * lerp); single_mie_scattering = IrradianceSpectrum( texture(single_mie_scattering_texture, uvw0) * (1.0 - lerp) + texture(single_mie_scattering_texture, uvw1) * lerp); #endif // COMBINED_SCATTERING_TEXTURES return scattering; } // @shotamatsuda: Added for reading higher-order scattering texture. #ifdef HAS_HIGHER_ORDER_SCATTERING_TEXTURE IrradianceSpectrum GetScattering( const AtmosphereParameters atmosphere, const ReducedScatteringTexture scattering_texture, const Length r, const Number mu, const Number mu_s, const Number nu, const bool ray_r_mu_intersects_ground) { vec4 uvwz = GetScatteringTextureUvwzFromRMuMuSNu( atmosphere, r, mu, mu_s, nu, ray_r_mu_intersects_ground); Number tex_coord_x = uvwz.x * Number(SCATTERING_TEXTURE_NU_SIZE - 1); Number tex_x = floor(tex_coord_x); Number lerp = tex_coord_x - tex_x; vec3 uvw0 = vec3((tex_x + uvwz.y) / Number(SCATTERING_TEXTURE_NU_SIZE), uvwz.z, uvwz.w); vec3 uvw1 = vec3((tex_x + 1.0 + uvwz.y) / Number(SCATTERING_TEXTURE_NU_SIZE), uvwz.z, uvwz.w); IrradianceSpectrum scattering = IrradianceSpectrum( texture(scattering_texture, uvw0) * (1.0 - lerp) + texture(scattering_texture, uvw1) * lerp); return scattering; } #endif // HAS_HIGHER_ORDER_SCATTERING_TEXTURE RadianceSpectrum GetSkyRadiance( const AtmosphereParameters atmosphere, const TransmittanceTexture transmittance_texture, const ReducedScatteringTexture scattering_texture, const ReducedScatteringTexture single_mie_scattering_texture, Position camera, const Direction view_ray, const Length shadow_length, const Direction sun_direction, out DimensionlessSpectrum transmittance) { // Compute the distance to the top atmosphere boundary along the view ray, // assuming the viewer is in space (or NaN if the view ray does not intersect // the atmosphere). Length r = length(camera); Length rmu = dot(camera, view_ray); // @shotamatsuda: Use SafeSqrt instead. // See: https://github.com/takram-design-engineering/three-geospatial/pull/26 Length distance_to_top_atmosphere_boundary = -rmu - SafeSqrt(rmu * rmu - r * r + atmosphere.top_radius * atmosphere.top_radius); // If the viewer is in space and the view ray intersects the atmosphere, move // the viewer to the top atmosphere boundary (along the view ray): if (distance_to_top_atmosphere_boundary > 0.0 * m) { camera = camera + view_ray * distance_to_top_atmosphere_boundary; r = atmosphere.top_radius; rmu += distance_to_top_atmosphere_boundary; } else if (r > atmosphere.top_radius) { // If the view ray does not intersect the atmosphere, simply return 0. transmittance = DimensionlessSpectrum(1.0); return RadianceSpectrum(0.0 * watt_per_square_meter_per_sr_per_nm); } // Compute the r, mu, mu_s and nu parameters needed for the texture lookups. Number mu = rmu / r; Number mu_s = dot(camera, sun_direction) / r; Number nu = dot(view_ray, sun_direction); // @shotamatsuda: For rendering points below the bottom atmosphere. #ifdef GROUND bool ray_r_mu_intersects_ground = RayIntersectsGround(atmosphere, r, mu); #else // GROUND bool ray_r_mu_intersects_ground = false; #endif // GROUND transmittance = ray_r_mu_intersects_ground ? DimensionlessSpectrum(0.0) : GetTransmittanceToTopAtmosphereBoundary( atmosphere, transmittance_texture, r, mu); IrradianceSpectrum single_mie_scattering; IrradianceSpectrum scattering; if (shadow_length == 0.0 * m) { scattering = GetCombinedScattering( atmosphere, scattering_texture, single_mie_scattering_texture, r, mu, mu_s, nu, ray_r_mu_intersects_ground, single_mie_scattering); } else { // Case of light shafts (shadow_length is the total length noted l in our // paper): we omit the scattering between the camera and the point at // distance l, by implementing Eq. (18) of the paper (shadow_transmittance // is the T(x,x_s) term, scattering is the S|x_s=x+lv term). Length d = shadow_length; Length r_p = ClampRadius(atmosphere, sqrt(d * d + 2.0 * r * mu * d + r * r)); Number mu_p = (r * mu + d) / r_p; Number mu_s_p = (r * mu_s + d * nu) / r_p; scattering = GetCombinedScattering( atmosphere, scattering_texture, single_mie_scattering_texture, r_p, mu_p, mu_s_p, nu, ray_r_mu_intersects_ground, single_mie_scattering); DimensionlessSpectrum shadow_transmittance = GetTransmittance(atmosphere, transmittance_texture, r, mu, shadow_length, ray_r_mu_intersects_ground); // @shotamatsuda: Occlude only single Rayleigh scattering by the shadow. #ifdef HAS_HIGHER_ORDER_SCATTERING_TEXTURE IrradianceSpectrum higher_order_scattering = GetScattering( atmosphere, higher_order_scattering_texture, r_p, mu_p, mu_s_p, nu, ray_r_mu_intersects_ground); IrradianceSpectrum single_scattering = scattering - higher_order_scattering; scattering = single_scattering * shadow_transmittance + higher_order_scattering; #else // HAS_HIGHER_ORDER_SCATTERING_TEXTURE scattering = scattering * shadow_transmittance; #endif // HAS_HIGHER_ORDER_SCATTERING_TEXTURE single_mie_scattering = single_mie_scattering * shadow_transmittance; } return scattering * RayleighPhaseFunction(nu) + single_mie_scattering * MiePhaseFunction(atmosphere.mie_phase_function_g, nu); } // @shotamatsuda: Returns the point on the ray closest to the origin. vec3 ClosestPointOnRay(const Position camera, const Position point) { Position ray = point - camera; Number t = clamp(-dot(camera, ray) / dot(ray, ray), 0.0, 1.0); return camera + t * ray; } vec2 RaySphereIntersections( const Position camera, const Direction direction, const Length radius) { float b = 2.0 * dot(direction, camera); float c = dot(camera, camera) - radius * radius; float discriminant = b * b - 4.0 * c; float Q = sqrt(discriminant); return vec2(-b - Q, -b + Q) * 0.5; } // @shotamatsuda: Clip the view ray at the bottom atmosphere boundary. bool ClipAtBottomAtmosphere( const AtmosphereParameters atmosphere, const Direction view_ray, inout Position camera, inout Position point) { const Length eps = 0.0; Length bottom_radius = atmosphere.bottom_radius + eps; Length r_camera = length(camera); Length r_point = length(point); bool camera_below = r_camera < bottom_radius; bool point_below = r_point < bottom_radius; vec2 t = RaySphereIntersections(camera, view_ray, bottom_radius); Position intersection = camera + view_ray * (camera_below ? t.y : t.x); camera = camera_below ? intersection : camera; point = point_below ? intersection : point; return camera_below && point_below; } RadianceSpectrum GetSkyRadianceToPoint( const AtmosphereParameters atmosphere, const TransmittanceTexture transmittance_texture, const ReducedScatteringTexture scattering_texture, const ReducedScatteringTexture single_mie_scattering_texture, Position camera, Position point, const Length shadow_length, const Direction sun_direction, out DimensionlessSpectrum transmittance) { // @shotamatsuda: Avoid artifacts when the ray does not intersect the top // atmosphere boundary. if (length(ClosestPointOnRay(camera, point)) > atmosphere.top_radius) { transmittance = vec3(1.0); return vec3(0.0); } Direction view_ray = normalize(point - camera); if (ClipAtBottomAtmosphere(atmosphere, view_ray, camera, point)) { transmittance = vec3(1.0); return vec3(0.0); } // Compute the distance to the top atmosphere boundary along the view ray, // assuming the viewer is in space (or NaN if the view ray does not intersect // the atmosphere). Length r = length(camera); Length rmu = dot(camera, view_ray); // @shotamatsuda: Use SafeSqrt instead. // See: https://github.com/takram-design-engineering/three-geospatial/pull/26 Length distance_to_top_atmosphere_boundary = -rmu - SafeSqrt(rmu * rmu - r * r + atmosphere.top_radius * atmosphere.top_radius); // If the viewer is in space and the view ray intersects the atmosphere, move // the viewer to the top atmosphere boundary (along the view ray): if (distance_to_top_atmosphere_boundary > 0.0 * m) { camera = camera + view_ray * distance_to_top_atmosphere_boundary; r = atmosphere.top_radius; rmu += distance_to_top_atmosphere_boundary; } // Compute the r, mu, mu_s and nu parameters for the first texture lookup. Number mu = rmu / r; Number mu_s = dot(camera, sun_direction) / r; Number nu = dot(view_ray, sun_direction); Length d = length(point - camera); bool ray_r_mu_intersects_ground = RayIntersectsGround(atmosphere, r, mu); // @shotamatsuda: Hack to avoid rendering artifacts near the horizon, due to // finite atmosphere texture resolution and finite floating point precision. // See: https://github.com/ebruneton/precomputed_atmospheric_scattering/pull/32 if (!ray_r_mu_intersects_ground) { Number mu_horizon = -SafeSqrt(1.0 - (atmosphere.bottom_radius * atmosphere.bottom_radius) / (r * r)); const Number eps = 0.004; mu = max(mu, mu_horizon + eps); } transmittance = GetTransmittance(atmosphere, transmittance_texture, r, mu, d, ray_r_mu_intersects_ground); IrradianceSpectrum single_mie_scattering; IrradianceSpectrum scattering = GetCombinedScattering( atmosphere, scattering_texture, single_mie_scattering_texture, r, mu, mu_s, nu, ray_r_mu_intersects_ground, single_mie_scattering); // Compute the r, mu, mu_s and nu parameters for the second texture lookup. // If shadow_length is not 0 (case of light shafts), we want to ignore the // scattering along the last shadow_length meters of the view ray, which we // do by subtracting shadow_length from d (this way scattering_p is equal to // the S|x_s=x_0-lv term in Eq. (17) of our paper). d = max(d - shadow_length, 0.0 * m); Length r_p = ClampRadius(atmosphere, sqrt(d * d + 2.0 * r * mu * d + r * r)); Number mu_p = (r * mu + d) / r_p; Number mu_s_p = (r * mu_s + d * nu) / r_p; IrradianceSpectrum single_mie_scattering_p; IrradianceSpectrum scattering_p = GetCombinedScattering( atmosphere, scattering_texture, single_mie_scattering_texture, r_p, mu_p, mu_s_p, nu, ray_r_mu_intersects_ground, single_mie_scattering_p); // Combine the lookup results to get the scattering between camera and point. DimensionlessSpectrum shadow_transmittance = transmittance; if (shadow_length > 0.0 * m) { // This is the T(x,x_s) term in Eq. (17) of our paper, for light shafts. shadow_transmittance = GetTransmittance(atmosphere, transmittance_texture, r, mu, d, ray_r_mu_intersects_ground); } // @shotamatsuda: Occlude only single Rayleigh scattering by the shadow. #ifdef HAS_HIGHER_ORDER_SCATTERING_TEXTURE IrradianceSpectrum higher_order_scattering = GetScattering( atmosphere, higher_order_scattering_texture, r, mu, mu_s, nu, ray_r_mu_intersects_ground); IrradianceSpectrum single_scattering = scattering - higher_order_scattering; IrradianceSpectrum higher_order_scattering_p = GetScattering( atmosphere, higher_order_scattering_texture, r_p, mu_p, mu_s_p, nu, ray_r_mu_intersects_ground); IrradianceSpectrum single_scattering_p = scattering_p - higher_order_scattering_p; scattering = single_scattering - shadow_transmittance * single_scattering_p + higher_order_scattering - transmittance * higher_order_scattering_p; #else // HAS_HIGHER_ORDER_SCATTERING_TEXTURE scattering = scattering - shadow_transmittance * scattering_p; #endif // HAS_HIGHER_ORDER_SCATTERING_TEXTURE single_mie_scattering = single_mie_scattering - shadow_transmittance * single_mie_scattering_p; #ifdef COMBINED_SCATTERING_TEXTURES single_mie_scattering = GetExtrapolatedSingleMieScattering( atmosphere, vec4(scattering, single_mie_scattering.r)); #endif // COMBINED_SCATTERING_TEXTURES // Hack to avoid rendering artifacts when the sun is below the horizon. single_mie_scattering = single_mie_scattering * smoothstep(Number(0.0), Number(0.01), mu_s); return scattering * RayleighPhaseFunction(nu) + single_mie_scattering * MiePhaseFunction(atmosphere.mie_phase_function_g, nu); } IrradianceSpectrum GetSunAndSkyIrradiance( const AtmosphereParameters atmosphere, const TransmittanceTexture transmittance_texture, const IrradianceTexture irradiance_texture, const Position point, const Direction normal, const Direction sun_direction, out IrradianceSpectrum sky_irradiance) { Length r = length(point); Number mu_s = dot(point, sun_direction) / r; // Indirect irradiance (approximated if the surface is not horizontal). sky_irradiance = GetIrradiance(atmosphere, irradiance_texture, r, mu_s) * (1.0 + dot(normal, point) / r) * 0.5; // Direct irradiance. return atmosphere.solar_irradiance * GetTransmittanceToSun( atmosphere, transmittance_texture, r, mu_s) * max(dot(normal, sun_direction), 0.0); } // @shotamatsuda: Added for the clouds. IrradianceSpectrum GetSunAndSkyScalarIrradiance( const AtmosphereParameters atmosphere, const TransmittanceTexture transmittance_texture, const IrradianceTexture irradiance_texture, const Position point, const Direction sun_direction, out IrradianceSpectrum sky_irradiance) { Length r = length(point); Number mu_s = dot(point, sun_direction) / r; // Indirect irradiance. Integral over sphere yields 2π. sky_irradiance = GetIrradiance(atmosphere, irradiance_texture, r, mu_s) * 2.0 * PI; // Direct irradiance. Omit the cosine term. return atmosphere.solar_irradiance * GetTransmittanceToSun(atmosphere, transmittance_texture, r, mu_s); } Luminance3 GetSolarLuminance() { return ATMOSPHERE.solar_irradiance / (PI * ATMOSPHERE.sun_angular_radius * ATMOSPHERE.sun_angular_radius) * SUN_SPECTRAL_RADIANCE_TO_LUMINANCE; } Luminance3 GetSkyLuminance( const Position camera, Direction view_ray, const Length shadow_length, const Direction sun_direction, out DimensionlessSpectrum transmittance) { return GetSkyRadiance(ATMOSPHERE, transmittance_texture, scattering_texture, single_mie_scattering_texture, camera, view_ray, shadow_length, sun_direction, transmittance) * SKY_SPECTRAL_RADIANCE_TO_LUMINANCE; } Luminance3 GetSkyLuminanceToPoint( const Position camera, const Position point, const Length shadow_length, const Direction sun_direction, out DimensionlessSpectrum transmittance) { return GetSkyRadianceToPoint(ATMOSPHERE, transmittance_texture, scattering_texture, single_mie_scattering_texture, camera, point, shadow_length, sun_direction, transmittance) * SKY_SPECTRAL_RADIANCE_TO_LUMINANCE; } Illuminance3 GetSunAndSkyIlluminance( const Position p, const Direction normal, const Direction sun_direction, out IrradianceSpectrum sky_irradiance) { IrradianceSpectrum sun_irradiance = GetSunAndSkyIrradiance( ATMOSPHERE, transmittance_texture, irradiance_texture, p, normal, sun_direction, sky_irradiance); sky_irradiance *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE; return sun_irradiance * SUN_SPECTRAL_RADIANCE_TO_LUMINANCE; } // @shotamatsuda: Added for the clouds. Illuminance3 GetSunAndSkyScalarIlluminance( const Position p, const Direction sun_direction, out IrradianceSpectrum sky_irradiance) { IrradianceSpectrum sun_irradiance = GetSunAndSkyScalarIrradiance( ATMOSPHERE, transmittance_texture, irradiance_texture, p, sun_direction, sky_irradiance); sky_irradiance *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE; return sun_irradiance * SUN_SPECTRAL_RADIANCE_TO_LUMINANCE; } #define GetSolarRadiance GetSolarLuminance #define GetSkyRadiance GetSkyLuminance #define GetSkyRadianceToPoint GetSkyLuminanceToPoint #define GetSunAndSkyIrradiance GetSunAndSkyIlluminance #define GetSunAndSkyScalarIrradiance GetSunAndSkyScalarIlluminance `, r = `// Based on: https://github.com/ebruneton/precomputed_atmospheric_scattering/blob/master/atmosphere/functions.glsl /** * Copyright (c) 2017 Eric Bruneton * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * Precomputed Atmospheric Scattering * Copyright (c) 2008 INRIA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ Number GetLayerDensity(const DensityProfileLayer layer, const Length altitude) { Number density = layer.exp_term * exp(layer.exp_scale * altitude) + layer.linear_term * altitude + layer.constant_term; return clamp(density, Number(0.0), Number(1.0)); } Number GetProfileDensity(const DensityProfile profile, const Length altitude) { DensityProfileLayer layers[2] = profile.layers; return altitude < layers[0].width ? GetLayerDensity(layers[0], altitude) : GetLayerDensity(layers[1], altitude); } Length ComputeOpticalLengthToTopAtmosphereBoundary( const AtmosphereParameters atmosphere, const DensityProfile profile, const Length r, const Number mu) { assert(r >= atmosphere.bottom_radius && r <= atmosphere.top_radius); assert(mu >= -1.0 && mu <= 1.0); // Number of intervals for the numerical integration. const int SAMPLE_COUNT = 500; // The integration step, i.e. the length of each integration interval. Length dx = DistanceToTopAtmosphereBoundary(atmosphere, r, mu) / Number(SAMPLE_COUNT); // Integration loop. Length result = 0.0 * m; for (int i = 0; i <= SAMPLE_COUNT; ++i) { Length d_i = Number(i) * dx; // Distance between the current sample point and the planet center. Length r_i = sqrt(d_i * d_i + 2.0 * r * mu * d_i + r * r); // Number density at the current sample point (divided by the number density // at the bottom of the atmosphere, yielding a dimensionless number). Number y_i = GetProfileDensity(profile, r_i - atmosphere.bottom_radius); // Sample weight (from the trapezoidal rule). Number weight_i = i == 0 || i == SAMPLE_COUNT ? 0.5 : 1.0; result += y_i * weight_i * dx; } return result; } DimensionlessSpectrum ComputeTransmittanceToTopAtmosphereBoundary( const AtmosphereParameters atmosphere, const Length r, const Number mu) { assert(r >= atmosphere.bottom_radius && r <= atmosphere.top_radius); assert(mu >= -1.0 && mu <= 1.0); vec3 optical_depth = ( atmosphere.rayleigh_scattering * ComputeOpticalLengthToTopAtmosphereBoundary( atmosphere, atmosphere.rayleigh_density, r, mu) + atmosphere.mie_extinction * ComputeOpticalLengthToTopAtmosphereBoundary( atmosphere, atmosphere.mie_density, r, mu) + atmosphere.absorption_extinction * ComputeOpticalLengthToTopAtmosphereBoundary( atmosphere, atmosphere.absorption_density, r, mu)); // @shotamatsuda: Added for the precomputation stage in half-float precision. #ifdef TRANSMITTANCE_PRECISION_LOG return optical_depth; #else // TRANSMITTANCE_PRECISION_LOG return exp(-optical_depth); #endif // TRANSMITTANCE_PRECISION_LOG } Number GetUnitRangeFromTextureCoord(const Number u, const int texture_size) { return (u - 0.5 / Number(texture_size)) / (1.0 - 1.0 / Number(texture_size)); } void GetRMuFromTransmittanceTextureUv(const AtmosphereParameters atmosphere, const vec2 uv, out Length r, out Number mu) { assert(uv.x >= 0.0 && uv.x <= 1.0); assert(uv.y >= 0.0 && uv.y <= 1.0); Number x_mu = GetUnitRangeFromTextureCoord(uv.x, TRANSMITTANCE_TEXTURE_WIDTH); Number x_r = GetUnitRangeFromTextureCoord(uv.y, TRANSMITTANCE_TEXTURE_HEIGHT); // Distance to top atmosphere boundary for a horizontal ray at ground level. Length H = sqrt(atmosphere.top_radius * atmosphere.top_radius - atmosphere.bottom_radius * atmosphere.bottom_radius); // Distance to the horizon, from which we can compute r: Length rho = H * x_r; r = sqrt(rho * rho + atmosphere.bottom_radius * atmosphere.bottom_radius); // Distance to the top atmosphere boundary for the ray (r,mu), and its minimum // and maximum values over all mu - obtained for (r,1) and (r,mu_horizon) - // from which we can recover mu: Length d_min = atmosphere.top_radius - r; Length d_max = rho + H; Length d = d_min + x_mu * (d_max - d_min); mu = d == 0.0 * m ? Number(1.0) : (H * H - rho * rho - d * d) / (2.0 * r * d); mu = ClampCosine(mu); } DimensionlessSpectrum ComputeTransmittanceToTopAtmosphereBoundaryTexture( const AtmosphereParameters atmosphere, const vec2 frag_coord) { const vec2 TRANSMITTANCE_TEXTURE_SIZE = vec2(TRANSMITTANCE_TEXTURE_WIDTH, TRANSMITTANCE_TEXTURE_HEIGHT); Length r; Number mu; GetRMuFromTransmittanceTextureUv( atmosphere, frag_coord / TRANSMITTANCE_TEXTURE_SIZE, r, mu); return ComputeTransmittanceToTopAtmosphereBoundary(atmosphere, r, mu); } void ComputeSingleScatteringIntegrand( const AtmosphereParameters atmosphere, const TransmittanceTexture transmittance_texture, const Length r, const Number mu, const Number mu_s, const Number nu, const Length d, const bool ray_r_mu_intersects_ground, out DimensionlessSpectrum rayleigh, out DimensionlessSpectrum mie) { Length r_d = ClampRadius(atmosphere, sqrt(d * d + 2.0 * r * mu * d + r * r)); Number mu_s_d = ClampCosine((r * mu_s + d * nu) / r_d); DimensionlessSpectrum transmittance = GetTransmittance( atmosphere, transmittance_texture, r, mu, d, ray_r_mu_intersects_ground) * GetTransmittanceToSun( atmosphere, transmittance_texture, r_d, mu_s_d); rayleigh = transmittance * GetProfileDensity( atmosphere.rayleigh_density, r_d - atmosphere.bottom_radius); mie = transmittance * GetProfileDensity( atmosphere.mie_density, r_d - atmosphere.bottom_radius); } Length DistanceToNearestAtmosphereBoundary(const AtmosphereParameters atmosphere, Length r, Number mu, bool ray_r_mu_intersects_ground) { if (ray_r_mu_intersects_ground) { return DistanceToBottomAtmosphereBoundary(atmosphere, r, mu); } else { return DistanceToTopAtmosphereBoundary(atmosphere, r, mu); } } void ComputeSingleScattering( const AtmosphereParameters atmosphere, const TransmittanceTexture transmittance_texture, const Length r, const Number mu, const Number mu_s, const Number nu, const bool ray_r_mu_intersects_ground, out IrradianceSpectrum rayleigh, out IrradianceSpectrum mie) { assert(r >= atmosphere.bottom_radius && r <= atmosphere.top_radius); assert(mu >= -1.0 && mu <= 1.0); assert(mu_s >= -1.0 && mu_s <= 1.0); assert(nu >= -1.0 && nu <= 1.0); // Number of intervals for the numerical integration. const int SAMPLE_COUNT = 50; // The integration step, i.e. the length of each integration interval. Length dx = DistanceToNearestAtmosphereBoundary(atmosphere, r, mu, ray_r_mu_intersects_ground) / Number(SAMPLE_COUNT); // Integration loop. DimensionlessSpectrum rayleigh_sum = DimensionlessSpectrum(0.0); DimensionlessSpectrum mie_sum = DimensionlessSpectrum(0.0); for (int i = 0; i <= SAMPLE_COUNT; ++i) { Length d_i = Number(i) * dx; // The Rayleigh and Mie single scattering at the current sample point. DimensionlessSpectrum rayleigh_i; DimensionlessSpectrum mie_i; ComputeSingleScatteringIntegrand(atmosphere, transmittance_texture, r, mu, mu_s, nu, d_i, ray_r_mu_intersects_ground, rayleigh_i, mie_i); // Sample weight (from the trapezoidal rule). Number weight_i = (i == 0 || i == SAMPLE_COUNT) ? 0.5 : 1.0; rayleigh_sum += rayleigh_i * weight_i; mie_sum += mie_i * weight_i; } rayleigh = rayleigh_sum * dx * atmospher