@takram/three-atmosphere
Version:
A Three.js and R3F implementation of Precomputed Atmospheric Scattering
539 lines (506 loc) • 18.2 kB
JavaScript
"use strict";const n=`// Based on the following work and adapted to Three.js.
// This file includes runtime functions only. Please refer to Bruneton's source
// code for the whole picture. It has detailed comments.
// 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.
*/
float ClampCosine(float mu) {
return clamp(mu, float(-1.0), float(1.0));
}
float ClampDistance(float d) {
return max(d, 0.0);
}
float ClampRadius(float r) {
return clamp(r, u_bottom_radius, u_top_radius);
}
float SafeSqrt(float a) {
return sqrt(max(a, 0.0));
}
float DistanceToTopAtmosphereBoundary(float r, float mu) {
float discriminant = r * r * (mu * mu - 1.0) + u_top_radius * u_top_radius;
return ClampDistance(-r * mu + SafeSqrt(discriminant));
}
bool RayIntersectsGround(float r, float mu) {
return mu < 0.0 && r * r * (mu * mu - 1.0) + u_bottom_radius * u_bottom_radius >= 0.0;
}
float GetTextureCoordFromUnitRange(float x, int texture_size) {
return 0.5 / float(texture_size) + x * (1.0 - 1.0 / float(texture_size));
}
vec2 GetTransmittanceTextureUvFromRMu(float r, float mu) {
float H = sqrt(u_top_radius * u_top_radius - u_bottom_radius * u_bottom_radius);
float rho = SafeSqrt(r * r - u_bottom_radius * u_bottom_radius);
float d = DistanceToTopAtmosphereBoundary(r, mu);
float d_min = u_top_radius - r;
float d_max = rho + H;
float x_mu = (d - d_min) / (d_max - d_min);
float x_r = rho / H;
return vec2(
GetTextureCoordFromUnitRange(x_mu, TRANSMITTANCE_TEXTURE_WIDTH),
GetTextureCoordFromUnitRange(x_r, TRANSMITTANCE_TEXTURE_HEIGHT)
);
}
vec3 GetTransmittanceToTopAtmosphereBoundary(
const sampler2D u_transmittance_texture,
float r,
float mu
) {
vec2 uv = GetTransmittanceTextureUvFromRMu(r, mu);
return vec3(texture(u_transmittance_texture, uv));
}
vec3 GetTransmittance(
const sampler2D u_transmittance_texture,
float r,
float mu,
float d,
bool ray_r_mu_intersects_ground
) {
float r_d = ClampRadius(sqrt(d * d + 2.0 * r * mu * d + r * r));
float mu_d = ClampCosine((r * mu + d) / r_d);
if (ray_r_mu_intersects_ground) {
return min(
GetTransmittanceToTopAtmosphereBoundary(u_transmittance_texture, r_d, -mu_d) /
GetTransmittanceToTopAtmosphereBoundary(u_transmittance_texture, r, -mu),
vec3(1.0)
);
} else {
return min(
GetTransmittanceToTopAtmosphereBoundary(u_transmittance_texture, r, mu) /
GetTransmittanceToTopAtmosphereBoundary(u_transmittance_texture, r_d, mu_d),
vec3(1.0)
);
}
}
vec3 GetTransmittanceToSun(const sampler2D u_transmittance_texture, float r, float mu_s) {
float sin_theta_h = u_bottom_radius / r;
float cos_theta_h = -sqrt(max(1.0 - sin_theta_h * sin_theta_h, 0.0));
return GetTransmittanceToTopAtmosphereBoundary(u_transmittance_texture, r, mu_s) *
smoothstep(
-sin_theta_h * u_sun_angular_radius,
sin_theta_h * u_sun_angular_radius,
mu_s - cos_theta_h
);
}
float RayleighPhaseFunction(float nu) {
float k = 3.0 / (16.0 * PI);
return k * (1.0 + nu * nu);
}
float MiePhaseFunction(float g, float nu) {
float k = 3.0 / (8.0 * PI) * (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(
float r,
float mu,
float mu_s,
float nu,
bool ray_r_mu_intersects_ground
) {
float H = sqrt(u_top_radius * u_top_radius - u_bottom_radius * u_bottom_radius);
float rho = SafeSqrt(r * r - u_bottom_radius * u_bottom_radius);
float u_r = GetTextureCoordFromUnitRange(rho / H, SCATTERING_TEXTURE_R_SIZE);
float r_mu = r * mu;
float discriminant = r_mu * r_mu - r * r + u_bottom_radius * u_bottom_radius;
float u_mu;
if (ray_r_mu_intersects_ground) {
float d = -r_mu - SafeSqrt(discriminant);
float d_min = r - u_bottom_radius;
float 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 {
float d = -r_mu + SafeSqrt(discriminant + H * H);
float d_min = u_top_radius - r;
float d_max = rho + H;
u_mu =
0.5 +
0.5 *
GetTextureCoordFromUnitRange((d - d_min) / (d_max - d_min), SCATTERING_TEXTURE_MU_SIZE / 2);
}
float d = DistanceToTopAtmosphereBoundary(u_bottom_radius, mu_s);
float d_min = u_top_radius - u_bottom_radius;
float d_max = H;
float a = (d - d_min) / (d_max - d_min);
float D = DistanceToTopAtmosphereBoundary(u_bottom_radius, u_mu_s_min);
float A = (D - d_min) / (d_max - d_min);
float u_mu_s = GetTextureCoordFromUnitRange(
max(1.0 - a / A, 0.0) / (1.0 + a),
SCATTERING_TEXTURE_MU_S_SIZE
);
float u_nu = (nu + 1.0) / 2.0;
return vec4(u_nu, u_mu_s, u_mu, u_r);
}
vec2 GetIrradianceTextureUvFromRMuS(float r, float mu_s) {
float x_r = (r - u_bottom_radius) / (u_top_radius - u_bottom_radius);
float x_mu_s = mu_s * 0.5 + 0.5;
return vec2(
GetTextureCoordFromUnitRange(x_mu_s, IRRADIANCE_TEXTURE_WIDTH),
GetTextureCoordFromUnitRange(x_r, IRRADIANCE_TEXTURE_HEIGHT)
);
}
vec3 GetIrradiance(const sampler2D u_irradiance_texture, float r, float mu_s) {
vec2 uv = GetIrradianceTextureUvFromRMuS(r, mu_s);
return vec3(texture(u_irradiance_texture, uv));
}
vec3 GetExtrapolatedSingleMieScattering(const vec4 scattering) {
if (scattering.r <= 0.0) {
return vec3(0.0);
}
return scattering.rgb *
scattering.a /
scattering.r *
(u_rayleigh_scattering.r / u_mie_scattering.r) *
(u_mie_scattering / u_rayleigh_scattering);
}
vec3 GetCombinedScattering(
const sampler3D u_scattering_texture,
const sampler3D u_single_mie_scattering_texture,
float r,
float mu,
float mu_s,
float nu,
bool ray_r_mu_intersects_ground,
out vec3 single_mie_scattering
) {
vec4 uvwz = GetScatteringTextureUvwzFromRMuMuSNu(r, mu, mu_s, nu, ray_r_mu_intersects_ground);
float tex_coord_x = uvwz.x * float(SCATTERING_TEXTURE_NU_SIZE - 1);
float tex_x = floor(tex_coord_x);
float lerp = tex_coord_x - tex_x;
vec3 uvw0 = vec3((tex_x + uvwz.y) / float(SCATTERING_TEXTURE_NU_SIZE), uvwz.z, uvwz.w);
vec3 uvw1 = vec3((tex_x + 1.0 + uvwz.y) / float(SCATTERING_TEXTURE_NU_SIZE), uvwz.z, uvwz.w);
vec4 combined_scattering =
texture(u_scattering_texture, uvw0) * (1.0 - lerp) + texture(u_scattering_texture, uvw1) * lerp;
vec3 scattering = vec3(combined_scattering);
single_mie_scattering = GetExtrapolatedSingleMieScattering(combined_scattering);
return scattering;
}
vec3 GetSkyRadiance(
const sampler2D u_transmittance_texture,
const sampler3D u_scattering_texture,
const sampler3D u_single_mie_scattering_texture,
vec3 camera,
const vec3 view_ray,
float shadow_length,
const vec3 sun_direction,
out vec3 transmittance
) {
float r = length(camera);
float rmu = dot(camera, view_ray);
float distance_to_top_atmosphere_boundary =
-rmu - SafeSqrt(rmu * rmu - r * r + u_top_radius * u_top_radius);
if (distance_to_top_atmosphere_boundary > 0.0) {
camera = camera + view_ray * distance_to_top_atmosphere_boundary;
r = u_top_radius;
rmu += distance_to_top_atmosphere_boundary;
} else if (r > u_top_radius) {
transmittance = vec3(1.0);
return vec3(0.0);
}
float mu = rmu / r;
float mu_s = dot(camera, sun_direction) / r;
float nu = dot(view_ray, sun_direction);
bool ray_r_mu_intersects_ground = RayIntersectsGround(r, mu);
transmittance = ray_r_mu_intersects_ground
? vec3(0.0)
: GetTransmittanceToTopAtmosphereBoundary(u_transmittance_texture, r, mu);
vec3 single_mie_scattering;
vec3 scattering;
if (shadow_length == 0.0) {
scattering = GetCombinedScattering(
u_scattering_texture,
u_single_mie_scattering_texture,
r,
mu,
mu_s,
nu,
ray_r_mu_intersects_ground,
single_mie_scattering
);
} else {
float d = shadow_length;
float r_p = ClampRadius(sqrt(d * d + 2.0 * r * mu * d + r * r));
float mu_p = (r * mu + d) / r_p;
float mu_s_p = (r * mu_s + d * nu) / r_p;
scattering = GetCombinedScattering(
u_scattering_texture,
u_single_mie_scattering_texture,
r_p,
mu_p,
mu_s_p,
nu,
ray_r_mu_intersects_ground,
single_mie_scattering
);
vec3 shadow_transmittance = GetTransmittance(
u_transmittance_texture,
r,
mu,
shadow_length,
ray_r_mu_intersects_ground
);
scattering = scattering * shadow_transmittance;
single_mie_scattering = single_mie_scattering * shadow_transmittance;
}
return scattering * RayleighPhaseFunction(nu) +
single_mie_scattering * MiePhaseFunction(u_mie_phase_function_g, nu);
}
vec3 GetSkyRadianceToPoint(
const sampler2D u_transmittance_texture,
const sampler3D u_scattering_texture,
const sampler3D u_single_mie_scattering_texture,
vec3 camera,
const vec3 point,
float shadow_length,
const vec3 sun_direction,
out vec3 transmittance
) {
vec3 view_ray = normalize(point - camera);
float r = length(camera);
float rmu = dot(camera, view_ray);
float distance_to_top_atmosphere_boundary =
-rmu - sqrt(rmu * rmu - r * r + u_top_radius * u_top_radius);
if (distance_to_top_atmosphere_boundary > 0.0) {
camera = camera + view_ray * distance_to_top_atmosphere_boundary;
r = u_top_radius;
rmu += distance_to_top_atmosphere_boundary;
}
float mu = rmu / r;
float mu_s = dot(camera, sun_direction) / r;
float nu = dot(view_ray, sun_direction);
float d = length(point - camera);
bool ray_r_mu_intersects_ground = RayIntersectsGround(r, mu);
// 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) {
float mu_horiz = -SafeSqrt(1.0 - u_bottom_radius / r * (u_bottom_radius / r));
mu = max(mu, mu_horiz + 0.004);
}
transmittance = GetTransmittance(u_transmittance_texture, r, mu, d, ray_r_mu_intersects_ground);
vec3 single_mie_scattering;
vec3 scattering = GetCombinedScattering(
u_scattering_texture,
u_single_mie_scattering_texture,
r,
mu,
mu_s,
nu,
ray_r_mu_intersects_ground,
single_mie_scattering
);
d = max(d - shadow_length, 0.0);
float r_p = ClampRadius(sqrt(d * d + 2.0 * r * mu * d + r * r));
float mu_p = (r * mu + d) / r_p;
float mu_s_p = (r * mu_s + d * nu) / r_p;
vec3 single_mie_scattering_p;
vec3 scattering_p = GetCombinedScattering(
u_scattering_texture,
u_single_mie_scattering_texture,
r_p,
mu_p,
mu_s_p,
nu,
ray_r_mu_intersects_ground,
single_mie_scattering_p
);
vec3 shadow_transmittance = transmittance;
if (shadow_length > 0.0) {
shadow_transmittance = GetTransmittance(
u_transmittance_texture,
r,
mu,
d,
ray_r_mu_intersects_ground
);
}
scattering = scattering - shadow_transmittance * scattering_p;
single_mie_scattering = single_mie_scattering - shadow_transmittance * single_mie_scattering_p;
single_mie_scattering = GetExtrapolatedSingleMieScattering(
vec4(scattering, single_mie_scattering.r)
);
single_mie_scattering = single_mie_scattering * smoothstep(float(0.0), float(0.01), mu_s);
return scattering * RayleighPhaseFunction(nu) +
single_mie_scattering * MiePhaseFunction(u_mie_phase_function_g, nu);
}
vec3 GetSunAndSkyIrradiance(
const sampler2D u_transmittance_texture,
const sampler2D u_irradiance_texture,
const vec3 point,
const vec3 sun_direction,
out vec3 sky_irradiance
) {
float r = length(point);
float mu_s = dot(point, sun_direction) / r;
sky_irradiance = GetIrradiance(u_irradiance_texture, r, mu_s);
return u_solar_irradiance * GetTransmittanceToSun(u_transmittance_texture, r, mu_s);
}
vec3 GetSunAndSkyIrradiance(
const sampler2D u_transmittance_texture,
const sampler2D u_irradiance_texture,
const vec3 point,
const vec3 normal,
const vec3 sun_direction,
out vec3 sky_irradiance
) {
float r = length(point);
float mu_s = dot(point, sun_direction) / r;
sky_irradiance =
GetIrradiance(u_irradiance_texture, r, mu_s) * (1.0 + dot(normal, point) / r) * 0.5;
return u_solar_irradiance *
GetTransmittanceToSun(u_transmittance_texture, r, mu_s) *
max(dot(normal, sun_direction), 0.0);
}
vec3 GetSolarRadiance() {
vec3 radiance = u_solar_irradiance / (PI * u_sun_angular_radius * u_sun_angular_radius);
radiance *= SUN_SPECTRAL_RADIANCE_TO_LUMINANCE;
return radiance;
}
vec3 GetSkyRadiance(
vec3 camera,
vec3 view_ray,
float shadow_length,
vec3 sun_direction,
out vec3 transmittance
) {
vec3 radiance = GetSkyRadiance(
u_transmittance_texture,
u_scattering_texture,
u_single_mie_scattering_texture,
camera,
view_ray,
shadow_length,
sun_direction,
transmittance
);
radiance *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE;
return radiance;
}
vec3 GetSkyRadianceToPoint(
vec3 camera,
vec3 point,
float shadow_length,
vec3 sun_direction,
out vec3 transmittance
) {
vec3 inscatter = GetSkyRadianceToPoint(
u_transmittance_texture,
u_scattering_texture,
u_single_mie_scattering_texture,
camera,
point,
shadow_length,
sun_direction,
transmittance
);
inscatter *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE;
return inscatter;
}
vec3 GetSunAndSkyIrradiance(vec3 point, vec3 sun_direction, out vec3 sky_irradiance) {
vec3 sun_irradiance = GetSunAndSkyIrradiance(
u_transmittance_texture,
u_irradiance_texture,
point,
sun_direction,
sky_irradiance
);
sun_irradiance *= SUN_SPECTRAL_RADIANCE_TO_LUMINANCE;
sky_irradiance *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE;
return sun_irradiance;
}
vec3 GetSunAndSkyIrradiance(vec3 point, vec3 normal, vec3 sun_direction, out vec3 sky_irradiance) {
vec3 sun_irradiance = GetSunAndSkyIrradiance(
u_transmittance_texture,
u_irradiance_texture,
point,
normal,
sun_direction,
sky_irradiance
);
sun_irradiance *= SUN_SPECTRAL_RADIANCE_TO_LUMINANCE;
sky_irradiance *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE;
return sun_irradiance;
}
`,t=`uniform vec3 u_solar_irradiance;
uniform float u_sun_angular_radius;
uniform float u_bottom_radius;
uniform float u_top_radius;
uniform vec3 u_rayleigh_scattering;
uniform vec3 u_mie_scattering;
uniform float u_mie_phase_function_g;
uniform float u_mu_s_min;
uniform sampler2D u_transmittance_texture;
uniform sampler3D u_scattering_texture;
uniform sampler3D u_single_mie_scattering_texture;
uniform sampler2D u_irradiance_texture;
`;exports._functions=n;exports._parameters=t;
//# sourceMappingURL=shared2.cjs.map