@takram/three-atmosphere
Version:
A Three.js and R3F implementation of Precomputed Atmospheric Scattering
1,103 lines (1,016 loc) • 68.7 kB
JavaScript
"use strict";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 * atmosph