UNPKG

@takram/three-atmosphere

Version:
601 lines (564 loc) 20.2 kB
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(const float mu) { return clamp(mu, -1.0, 1.0); } float ClampDistance(const float d) { return max(d, 0.0); } float ClampRadius(const float r) { return clamp(r, u_bottom_radius, u_top_radius); } float SafeSqrt(const float a) { return sqrt(max(a, 0.0)); } float DistanceToTopAtmosphereBoundary(const float r, const float mu) { float discriminant = r * r * (mu * mu - 1.0) + u_top_radius * u_top_radius; return ClampDistance(-r * mu + SafeSqrt(discriminant)); } float DistanceToBottomAtmosphereBoundary(const float r, const float mu) { float discriminant = r * r * (mu * mu - 1.0) + u_bottom_radius * u_bottom_radius; return ClampDistance(-r * mu - SafeSqrt(discriminant)); } bool RayIntersectsGround(const float r, const float mu) { return mu < 0.0 && r * r * (mu * mu - 1.0) + u_bottom_radius * u_bottom_radius >= 0.0; } float GetTextureCoordFromUnitRange(const float x, const int texture_size) { return 0.5 / float(texture_size) + x * (1.0 - 1.0 / float(texture_size)); } vec2 GetTransmittanceTextureUvFromRMu(const float r, const 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 transmittance_texture, const float r, const float mu ) { vec2 uv = GetTransmittanceTextureUvFromRMu(r, mu); return vec3(texture(transmittance_texture, uv)); } vec3 GetTransmittance( const sampler2D transmittance_texture, const float r, const float mu, const float d, const 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(transmittance_texture, r_d, -mu_d) / GetTransmittanceToTopAtmosphereBoundary(transmittance_texture, r, -mu), vec3(1.0) ); } else { return min( GetTransmittanceToTopAtmosphereBoundary(transmittance_texture, r, mu) / GetTransmittanceToTopAtmosphereBoundary(transmittance_texture, r_d, mu_d), vec3(1.0) ); } } vec3 GetTransmittanceToSun(const sampler2D transmittance_texture, const float r, const 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(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(const float nu) { float k = 3.0 / (16.0 * PI); return k * (1.0 + nu * nu); } float MiePhaseFunction(const float g, const 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( const float r, const float mu, const float mu_s, const float nu, const 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(const float r, const 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 irradiance_texture, const float r, const float mu_s) { vec2 uv = GetIrradianceTextureUvFromRMuS(r, mu_s); return vec3(texture(irradiance_texture, uv)); } vec3 GetExtrapolatedSingleMieScattering(const vec4 scattering) { if (scattering.r < 1e-5) { 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 scattering_texture, const sampler3D single_mie_scattering_texture, const float r, const float mu, const float mu_s, const float nu, const 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(scattering_texture, uvw0) * (1.0 - lerp) + texture(scattering_texture, uvw1) * lerp; vec3 scattering = vec3(combined_scattering); single_mie_scattering = GetExtrapolatedSingleMieScattering(combined_scattering); return scattering; } vec3 GetSkyRadiance( const sampler2D transmittance_texture, const sampler3D scattering_texture, const sampler3D single_mie_scattering_texture, vec3 camera, const vec3 view_ray, const 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(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 { // Use different points for Rayleigh and Mie scattering since a large shadow // length for Rayleigh scattering leads to an overly orange tint, which // doesn't work well with the clouds seemingly because their in-scattering // is an approximation for terrain. float rayleigh_shadow_length = min(shadow_length, u_max_rayleigh_shadow_length); float d = rayleigh_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( scattering_texture, single_mie_scattering_texture, r_p, mu_p, mu_s_p, nu, ray_r_mu_intersects_ground, single_mie_scattering ); vec3 rayleigh_transmittance = GetTransmittance( transmittance_texture, r, mu, rayleigh_shadow_length, ray_r_mu_intersects_ground ); d = shadow_length; r_p = ClampRadius(sqrt(d * d + 2.0 * r * mu * d + r * r)); mu_p = (r * mu + d) / r_p; mu_s_p = (r * mu_s + d * nu) / r_p; GetCombinedScattering( scattering_texture, single_mie_scattering_texture, r_p, mu_p, mu_s_p, nu, ray_r_mu_intersects_ground, single_mie_scattering ); vec3 mie_transmittance = GetTransmittance( transmittance_texture, r, mu, shadow_length, ray_r_mu_intersects_ground ); scattering = scattering * rayleigh_transmittance; single_mie_scattering = single_mie_scattering * mie_transmittance; } return scattering * RayleighPhaseFunction(nu) + single_mie_scattering * MiePhaseFunction(u_mie_phase_function_g, nu); } bool RayOutsideTopAtmosphereBoundary(const vec3 camera, const vec3 point, const float r) { if (r < u_top_radius || length(point) < u_top_radius) { return false; } vec3 ray = point - camera; float t = -clamp(dot(camera, ray) / dot(ray, ray), 0.0, 1.0); return length(camera + t * ray) > u_top_radius; } vec3 GetSkyRadianceToPoint( const sampler2D transmittance_texture, const sampler3D scattering_texture, const sampler3D single_mie_scattering_texture, vec3 camera, const vec3 point, const float shadow_length, const vec3 sun_direction, out vec3 transmittance ) { float r = length(camera); if (RayOutsideTopAtmosphereBoundary(camera, point, r)) { transmittance = vec3(1.0); return vec3(0.0); // Avoid artifacts } vec3 view_ray = normalize(point - 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(transmittance_texture, r, mu, d, ray_r_mu_intersects_ground); vec3 single_mie_scattering; vec3 scattering = GetCombinedScattering( scattering_texture, 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( scattering_texture, 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( 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(0.0, 0.01, mu_s); return scattering * RayleighPhaseFunction(nu) + single_mie_scattering * MiePhaseFunction(u_mie_phase_function_g, nu); } vec3 GetSunAndSkyIrradianceForParticle( const sampler2D transmittance_texture, const sampler2D 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; // Integral of (1+dot(n,p))/2 over sphere yields 2π. sky_irradiance = GetIrradiance(irradiance_texture, r, mu_s) * 2.0 * PI; // Sunlight is directional. Just omit the cosine term. return u_solar_irradiance * GetTransmittanceToSun(transmittance_texture, r, mu_s); } vec3 GetSunAndSkyIrradiance( const sampler2D transmittance_texture, const sampler2D 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(irradiance_texture, r, mu_s) * (1.0 + dot(normal, point) / r) * 0.5; return u_solar_irradiance * GetTransmittanceToSun(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); #ifdef PHOTOMETRIC radiance *= SUN_SPECTRAL_RADIANCE_TO_LUMINANCE; #endif // PHOTOMETRIC return radiance; } vec3 GetSkyRadiance( const vec3 camera, const vec3 view_ray, const float shadow_length, const 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 ); #ifdef PHOTOMETRIC radiance *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE; #endif // PHOTOMETRIC return radiance; } vec3 GetSkyRadianceToPoint( const vec3 camera, const vec3 point, const float shadow_length, const 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 ); #ifdef PHOTOMETRIC inscatter *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE; #endif // PHOTOMETRIC return inscatter; } vec3 GetSunAndSkyIrradianceForParticle( const vec3 point, const vec3 sun_direction, out vec3 sky_irradiance ) { vec3 sun_irradiance = GetSunAndSkyIrradianceForParticle( u_transmittance_texture, u_irradiance_texture, point, sun_direction, sky_irradiance ); #ifdef PHOTOMETRIC sun_irradiance *= SUN_SPECTRAL_RADIANCE_TO_LUMINANCE; sky_irradiance *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE; #endif // PHOTOMETRIC return sun_irradiance; } vec3 GetSunAndSkyIrradiance( const vec3 point, const vec3 normal, const vec3 sun_direction, out vec3 sky_irradiance ) { vec3 sun_irradiance = GetSunAndSkyIrradiance( u_transmittance_texture, u_irradiance_texture, point, normal, sun_direction, sky_irradiance ); #ifdef PHOTOMETRIC sun_irradiance *= SUN_SPECTRAL_RADIANCE_TO_LUMINANCE; sky_irradiance *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE; #endif // PHOTOMETRIC 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 float u_max_rayleigh_shadow_length; uniform sampler2D u_transmittance_texture; uniform sampler3D u_scattering_texture; uniform sampler3D u_single_mie_scattering_texture; uniform sampler2D u_irradiance_texture; `; export { n as _, t as a }; //# sourceMappingURL=shared2.js.map