Browse Source

Improve sky pass, improve parameterization of atmospheric scattering, add more atmospheric scattering-related functions to the physics::atmosphere namespace

master
C. J. Howard 3 years ago
parent
commit
f26552c3ad
8 changed files with 307 additions and 257 deletions
  1. +16
    -4
      src/ecs/components/atmosphere-component.hpp
  2. +48
    -68
      src/ecs/systems/astronomy-system.cpp
  3. +0
    -1
      src/game/bootloader.cpp
  4. +15
    -20
      src/game/states/play-state.cpp
  5. +115
    -0
      src/physics/atmosphere.hpp
  6. +19
    -0
      src/physics/light/phase.hpp
  7. +70
    -128
      src/renderer/passes/sky-pass.cpp
  8. +24
    -36
      src/renderer/passes/sky-pass.hpp

+ 16
- 4
src/ecs/components/atmosphere-component.hpp View File

@ -30,17 +30,29 @@ struct atmosphere_component
/// Altitude of the outer atmosphere, in meters. /// Altitude of the outer atmosphere, in meters.
double exosphere_altitude; double exosphere_altitude;
/// Atmospheric index of refraction at sea level.
double index_of_refraction;
/// Molecular density of Rayleigh particles at sea level.
double rayleigh_density;
/// Molecular density of Mie particles at sea level.
double mie_density;
/// Rayleigh scale height, in meters. /// Rayleigh scale height, in meters.
double rayleigh_scale_height; double rayleigh_scale_height;
/// Mie scale height, in meters. /// Mie scale height, in meters.
double mie_scale_height; double mie_scale_height;
/// (Dependent) Rayleigh scattering coefficients
double3 rayleigh_scattering_coefficients;
/// Mie phase function asymmetry factor.
double mie_asymmetry;
/// (Dependent) Rayleigh scattering coefficients at sea level.
double3 rayleigh_scattering;
/// (Dependent) Mie scattering coefficients
double3 mie_scattering_coefficients;
/// (Dependent) Mie scattering coefficients at sea level.
double3 mie_scattering;
}; };
} // namespace ecs } // namespace ecs

+ 48
- 68
src/ecs/systems/astronomy-system.cpp View File

@ -30,45 +30,12 @@
#include "physics/light/blackbody.hpp" #include "physics/light/blackbody.hpp"
#include "physics/light/photometry.hpp" #include "physics/light/photometry.hpp"
#include "physics/light/luminosity.hpp" #include "physics/light/luminosity.hpp"
#include "physics/atmosphere.hpp"
#include "geom/cartesian.hpp" #include "geom/cartesian.hpp"
#include <iostream> #include <iostream>
namespace ecs { namespace ecs {
/**
* Approximates the density of exponentially-distributed atmospheric particles between two points using the trapezoidal rule.
*
* @param a Start point.
* @param b End point.
* @param r Radius of the planet.
* @param sh Scale height of the atmospheric particles.
* @param n Number of samples.
*/
template <class T>
T optical_depth(const math::vector3<T>& a, const math::vector3<T>& b, T r, T sh, std::size_t n)
{
T inverse_sh = T(-1) / sh;
T h = math::length(b - a) / T(n);
math::vector3<T> dy = (b - a) / T(n);
math::vector3<T> y = a + dy;
T f_x = std::exp((length(a) - r) * inverse_sh);
T f_y = std::exp((length(y) - r) * inverse_sh);
T sum = (f_x + f_y);
for (std::size_t i = 1; i < n; ++i)
{
f_x = f_y;
y += dy;
f_y = std::exp((length(y) - r) * inverse_sh);
sum += (f_x + f_y);
}
return sum / T(2) * h;
}
template <class T> template <class T>
math::vector3<T> transmittance(T depth_r, T depth_m, T depth_o, const math::vector3<T>& beta_r, const math::vector3<T>& beta_m) math::vector3<T> transmittance(T depth_r, T depth_m, T depth_o, const math::vector3<T>& beta_r, const math::vector3<T>& beta_m)
{ {
@ -84,16 +51,6 @@ math::vector3 transmittance(T depth_r, T depth_m, T depth_o, const math::vect
return t; return t;
} }
double calc_beta_r(double wavelength, double ior, double density)
{
double wavelength2 = wavelength * wavelength;
double ior2m1 = ior * ior - 1.0;
double num = 8.0 * (math::pi<double> * math::pi<double> * math::pi<double>) * ior2m1 * ior2m1;
double den = 3.0 * density * (wavelength2 * wavelength2);
return num / den;
}
astronomy_system::astronomy_system(ecs::registry& registry): astronomy_system::astronomy_system(ecs::registry& registry):
entity_system(registry), entity_system(registry),
universal_time(0.0), universal_time(0.0),
@ -153,10 +110,12 @@ void astronomy_system::update(double t, double dt)
transform.local.translation = math::type_cast<float>(r_topocentric); transform.local.translation = math::type_cast<float>(r_topocentric);
}); });
const double earth_radius = 6.3781e6;
// Update blackbody lighting // Update blackbody lighting
registry.view<blackbody_component, orbit_component>().each( registry.view<blackbody_component, orbit_component>().each(
[&](ecs::entity entity, auto& blackbody, auto& orbit) [&](ecs::entity entity, auto& blackbody, auto& orbit)
{
{
// Calculate blackbody inertial basis // Calculate blackbody inertial basis
double3 blackbody_forward_inertial = math::normalize(reference_orbit.state.r - orbit.state.r); double3 blackbody_forward_inertial = math::normalize(reference_orbit.state.r - orbit.state.r);
double3 blackbody_up_inertial = {0, 0, 1}; double3 blackbody_up_inertial = {0, 0, 1};
@ -167,8 +126,7 @@ void astronomy_system::update(double t, double dt)
double3 blackbody_up_topocentric = inertial_to_topocentric.rotation * blackbody_up_inertial; double3 blackbody_up_topocentric = inertial_to_topocentric.rotation * blackbody_up_inertial;
// Calculate distance from observer to blackbody // Calculate distance from observer to blackbody
const double meters_per_au = 1.496e+11;
double blackbody_distance = math::length(blackbody_position_topocentric) * meters_per_au;
double blackbody_distance = math::length(blackbody_position_topocentric);
// Calculate blackbody illuminance according to distance // Calculate blackbody illuminance according to distance
double blackbody_illuminance = blackbody.luminous_intensity / (blackbody_distance * blackbody_distance); double blackbody_illuminance = blackbody.luminous_intensity / (blackbody_distance * blackbody_distance);
@ -181,17 +139,14 @@ void astronomy_system::update(double t, double dt)
{ {
const ecs::atmosphere_component& atmosphere = this->registry.get<ecs::atmosphere_component>(reference_body); const ecs::atmosphere_component& atmosphere = this->registry.get<ecs::atmosphere_component>(reference_body);
const double earth_radius_au = 4.26352e-5;
const double earth_radius_m = earth_radius_au * meters_per_au;
// Altitude of observer in meters // Altitude of observer in meters
geom::ray<double> sample_ray; geom::ray<double> sample_ray;
sample_ray.origin = {0, observer_location[0] * meters_per_au, 0};
sample_ray.origin = {0, observer_location[0], 0};
sample_ray.direction = math::normalize(blackbody_position_topocentric); sample_ray.direction = math::normalize(blackbody_position_topocentric);
geom::sphere<double> exosphere; geom::sphere<double> exosphere;
exosphere.center = {0, 0, 0}; exosphere.center = {0, 0, 0};
exosphere.radius = earth_radius_m + atmosphere.exosphere_altitude;
exosphere.radius = earth_radius + atmosphere.exosphere_altitude;
auto intersection_result = geom::ray_sphere_intersection(sample_ray, exosphere); auto intersection_result = geom::ray_sphere_intersection(sample_ray, exosphere);
@ -200,11 +155,11 @@ void astronomy_system::update(double t, double dt)
double3 sample_start = sample_ray.origin; double3 sample_start = sample_ray.origin;
double3 sample_end = sample_ray.extrapolate(std::get<2>(intersection_result)); double3 sample_end = sample_ray.extrapolate(std::get<2>(intersection_result));
double optical_depth_r = optical_depth(sample_start, sample_end, earth_radius_m, atmosphere.rayleigh_scale_height, 32);
double optical_depth_k = optical_depth(sample_start, sample_end, earth_radius_m, atmosphere.mie_scale_height, 32);
double optical_depth_r = physics::atmosphere::optical_depth(sample_start, sample_end, earth_radius, atmosphere.rayleigh_scale_height, 32);
double optical_depth_k = physics::atmosphere::optical_depth(sample_start, sample_end, earth_radius, atmosphere.mie_scale_height, 32);
double optical_depth_o = 0.0; double optical_depth_o = 0.0;
double3 attenuation = transmittance(optical_depth_r, optical_depth_k, optical_depth_o, atmosphere.rayleigh_scattering_coefficients, atmosphere.mie_scattering_coefficients);
double3 attenuation = transmittance(optical_depth_r, optical_depth_k, optical_depth_o, atmosphere.rayleigh_scattering, atmosphere.mie_scattering);
// Attenuate blackbody color // Attenuate blackbody color
blackbody_color *= attenuation; blackbody_color *= attenuation;
@ -214,7 +169,7 @@ void astronomy_system::update(double t, double dt)
if (sun_light != nullptr) if (sun_light != nullptr)
{ {
// Update blackbody light transform // Update blackbody light transform
sun_light->set_translation(math::type_cast<float>(blackbody_position_topocentric));
sun_light->set_translation(math::normalize(math::type_cast<float>(blackbody_position_topocentric)));
sun_light->set_rotation sun_light->set_rotation
( (
math::look_rotation math::look_rotation
@ -228,11 +183,14 @@ void astronomy_system::update(double t, double dt)
sun_light->set_color(math::type_cast<float>(blackbody_color)); sun_light->set_color(math::type_cast<float>(blackbody_color));
sun_light->set_intensity(static_cast<float>(blackbody_illuminance)); sun_light->set_intensity(static_cast<float>(blackbody_illuminance));
// Pass blackbody params to sky pas
// Upload blackbody params to sky pass
if (this->sky_pass) if (this->sky_pass)
{ {
this->sky_pass->set_sun_object(sun_light);
this->sky_pass->set_sun_position(math::type_cast<float>(blackbody_position_topocentric));
this->sky_pass->set_sun_color(math::type_cast<float>(blackbody.color * blackbody_illuminance)); this->sky_pass->set_sun_color(math::type_cast<float>(blackbody.color * blackbody_illuminance));
float angular_radius = 2.0 * std::atan2(2.0 * blackbody.radius, 2.0 * blackbody_distance);
this->sky_pass->set_sun_angular_radius(angular_radius);
} }
} }
}); });
@ -240,6 +198,7 @@ void astronomy_system::update(double t, double dt)
// Update sky pass topocentric frame // Update sky pass topocentric frame
if (sky_pass != nullptr) if (sky_pass != nullptr)
{ {
// Upload topocentric frame to sky pass
sky_pass->set_topocentric_frame sky_pass->set_topocentric_frame
( (
physics::frame<float> physics::frame<float>
@ -248,6 +207,21 @@ void astronomy_system::update(double t, double dt)
math::type_cast<float>(inertial_to_topocentric.rotation) math::type_cast<float>(inertial_to_topocentric.rotation)
} }
); );
// Upload observer altitude to sky pass
float observer_altitude = observer_location[0] - earth_radius;
sky_pass->set_observer_altitude(observer_altitude);
// Upload atmosphere params to sky pass
if (this->registry.has<ecs::atmosphere_component>(reference_body))
{
const ecs::atmosphere_component& atmosphere = this->registry.get<ecs::atmosphere_component>(reference_body);
sky_pass->set_scale_heights(atmosphere.rayleigh_scale_height, atmosphere.mie_scale_height);
sky_pass->set_scattering_coefficients(math::type_cast<float>(atmosphere.rayleigh_scattering), math::type_cast<float>(atmosphere.mie_scattering));
sky_pass->set_mie_asymmetry(atmosphere.mie_asymmetry);
sky_pass->set_atmosphere_radii(earth_radius, earth_radius + atmosphere.exosphere_altitude);
}
} }
} }
@ -361,23 +335,29 @@ void astronomy_system::on_atmosphere_construct(ecs::registry& registry, ecs::ent
void astronomy_system::on_atmosphere_replace(ecs::registry& registry, ecs::entity entity, ecs::atmosphere_component& atmosphere) void astronomy_system::on_atmosphere_replace(ecs::registry& registry, ecs::entity entity, ecs::atmosphere_component& atmosphere)
{ {
// Calculate polarization factors
const double rayleigh_polarization = physics::atmosphere::polarization(atmosphere.index_of_refraction, atmosphere.rayleigh_density);
const double mie_polarization = physics::atmosphere::polarization(atmosphere.index_of_refraction, atmosphere.mie_density);
// ACEScg wavelengths determined by matching wavelengths to XYZ, transforming XYZ to ACEScg, then selecting the max wavelengths for R, G, and B. // ACEScg wavelengths determined by matching wavelengths to XYZ, transforming XYZ to ACEScg, then selecting the max wavelengths for R, G, and B.
const double3 acescg_wavelengths_nm = {600.0, 540.0, 450.0};
const double3 acescg_wavelengths_m = acescg_wavelengths_nm * 1.0e-9;
const double3 acescg_wavelengths = {600.0e-9, 540.0e-9, 450.0e-9};
// Calculate Rayleigh scattering coefficients // Calculate Rayleigh scattering coefficients
const double air_ior = 1.0003;
const double molecular_density = 2.545e25;
double3 beta_r;
atmosphere.rayleigh_scattering_coefficients =
atmosphere.rayleigh_scattering =
{ {
calc_beta_r(acescg_wavelengths_m.x, air_ior, molecular_density),
calc_beta_r(acescg_wavelengths_m.y, air_ior, molecular_density),
calc_beta_r(acescg_wavelengths_m.z, air_ior, molecular_density)
physics::atmosphere::scatter_rayleigh(acescg_wavelengths.x, atmosphere.rayleigh_density, rayleigh_polarization),
physics::atmosphere::scatter_rayleigh(acescg_wavelengths.y, atmosphere.rayleigh_density, rayleigh_polarization),
physics::atmosphere::scatter_rayleigh(acescg_wavelengths.z, atmosphere.rayleigh_density, rayleigh_polarization)
}; };
// Calculate Mie scattering coefficients // Calculate Mie scattering coefficients
atmosphere.mie_scattering_coefficients = {2.0e-6, 2.0e-6, 2.0e-6};
const double mie_scattering = physics::atmosphere::scatter_mie(atmosphere.mie_density, mie_polarization);
atmosphere.mie_scattering =
{
mie_scattering,
mie_scattering,
mie_scattering
};
} }
} // namespace ecs } // namespace ecs

+ 0
- 1
src/game/bootloader.cpp View File

@ -503,7 +503,6 @@ void setup_rendering(game_context* ctx)
ctx->overworld_sky_pass = new sky_pass(ctx->rasterizer, ctx->framebuffer_hdr, ctx->resource_manager); ctx->overworld_sky_pass = new sky_pass(ctx->rasterizer, ctx->framebuffer_hdr, ctx->resource_manager);
ctx->app->get_event_dispatcher()->subscribe<mouse_moved_event>(ctx->overworld_sky_pass); ctx->app->get_event_dispatcher()->subscribe<mouse_moved_event>(ctx->overworld_sky_pass);
ctx->overworld_sky_pass->set_enabled(false); ctx->overworld_sky_pass->set_enabled(false);
ctx->overworld_sky_pass->set_blue_noise_map(blue_noise_map);
ctx->overworld_material_pass = new material_pass(ctx->rasterizer, ctx->framebuffer_hdr, ctx->resource_manager); ctx->overworld_material_pass = new material_pass(ctx->rasterizer, ctx->framebuffer_hdr, ctx->resource_manager);
ctx->overworld_material_pass->set_fallback_material(ctx->fallback_material); ctx->overworld_material_pass->set_fallback_material(ctx->fallback_material);
ctx->overworld_material_pass->shadow_map_pass = ctx->overworld_shadow_map_pass; ctx->overworld_material_pass->shadow_map_pass = ctx->overworld_shadow_map_pass;

+ 15
- 20
src/game/states/play-state.cpp View File

@ -93,15 +93,6 @@ void play_state_enter(game_context* ctx)
sky_pass->set_sky_model(ctx->resource_manager->load<model>("sky-dome.mdl")); sky_pass->set_sky_model(ctx->resource_manager->load<model>("sky-dome.mdl"));
sky_pass->set_moon_model(ctx->resource_manager->load<model>("moon.mdl")); sky_pass->set_moon_model(ctx->resource_manager->load<model>("moon.mdl"));
sky_pass->set_horizon_color({1.1f, 1.1f, 1.1f});
sky_pass->set_zenith_color({0.0f, 0.0f, 0.0f});
sky_pass->set_time_of_day(0.0f);
sky_pass->set_julian_day(0.0f);
sky_pass->set_observer_location(4.26352e-5, ctx->biome->location[0], ctx->biome->location[1]);
sky_pass->set_moon_angular_radius(math::radians(1.0f));
sky_pass->set_sun_angular_radius(math::radians(1.0f));
sky_pass->set_sky_gradient(resource_manager->load<gl::texture_2d>("sky-gradient.tex"), resource_manager->load<gl::texture_2d>("sky-gradient2.tex"));
// Create sun // Create sun
auto sun_entity = ecs_registry.create(); auto sun_entity = ecs_registry.create();
{ {
@ -130,7 +121,7 @@ void play_state_enter(game_context* ctx)
auto earth_entity = ecs_registry.create(); auto earth_entity = ecs_registry.create();
{ {
ecs::orbit_component orbit; ecs::orbit_component orbit;
orbit.elements.a = 1.00000261;
orbit.elements.a = 1.496e+11;
orbit.elements.e = 0.01671123; orbit.elements.e = 0.01671123;
orbit.elements.i = math::radians(-0.00001531); orbit.elements.i = math::radians(-0.00001531);
orbit.elements.raan = math::radians(0.0); orbit.elements.raan = math::radians(0.0);
@ -138,11 +129,16 @@ void play_state_enter(game_context* ctx)
orbit.elements.w = longitude_periapsis - orbit.elements.raan; orbit.elements.w = longitude_periapsis - orbit.elements.raan;
orbit.elements.ta = math::radians(100.46457166) - longitude_periapsis; orbit.elements.ta = math::radians(100.46457166) - longitude_periapsis;
const double earth_radius_m = 6378e3;
ecs::atmosphere_component atmosphere; ecs::atmosphere_component atmosphere;
atmosphere.exosphere_altitude = 80e3;
atmosphere.exosphere_altitude = 100e3;
atmosphere.index_of_refraction = 1.0003;
atmosphere.rayleigh_density = 2.545e25;
atmosphere.mie_density = 14.8875;
atmosphere.rayleigh_scale_height = 8000.0; atmosphere.rayleigh_scale_height = 8000.0;
atmosphere.mie_scale_height = 1200.0; atmosphere.mie_scale_height = 1200.0;
atmosphere.mie_asymmetry = 0.8;
ecs::transform_component transform; ecs::transform_component transform;
transform.local = math::identity_transform<float>; transform.local = math::identity_transform<float>;
@ -160,12 +156,12 @@ void play_state_enter(game_context* ctx)
ctx->overworld_scene->add_object(ambient); ctx->overworld_scene->add_object(ambient);
scene::directional_light* sun = new scene::directional_light(); scene::directional_light* sun = new scene::directional_light();
sun->set_intensity(1000.0f);
sun->set_light_texture(resource_manager->load<gl::texture_2d>("forest-gobo.tex"));
sun->set_light_texture_scale({2000, 2000});
sun->set_light_texture_opacity(0.925f);
sun->look_at({2, 1, 0}, {0, 0, 0}, {0, 0, 1});
sun->update_tweens();
//sun->set_intensity(1000.0f);
//sun->set_light_texture(resource_manager->load<gl::texture_2d>("forest-gobo.tex"));
//sun->set_light_texture_scale({2000, 2000});
//sun->set_light_texture_opacity(0.925f);
//sun->look_at({2, 1, 0}, {0, 0, 0}, {0, 0, 1});
//sun->update_tweens();
ctx->overworld_scene->add_object(sun); ctx->overworld_scene->add_object(sun);
ctx->overworld_shadow_map_pass->set_light(sun); ctx->overworld_shadow_map_pass->set_light(sun);
@ -175,10 +171,9 @@ void play_state_enter(game_context* ctx)
ctx->orbit_system->set_universal_time(universal_time); ctx->orbit_system->set_universal_time(universal_time);
// Set astronomy system observation parameters // Set astronomy system observation parameters
const double earth_radius_au = 4.2635e-5;
ctx->astronomy_system->set_reference_body(earth_entity); ctx->astronomy_system->set_reference_body(earth_entity);
ctx->astronomy_system->set_reference_body_axial_tilt(math::radians(23.4393)); ctx->astronomy_system->set_reference_body_axial_tilt(math::radians(23.4393));
ctx->astronomy_system->set_observer_location(double3{4.26352e-5, math::radians(0.0f), math::radians(0.0f)});
ctx->astronomy_system->set_observer_location(double3{6.3781e6, math::radians(0.0f), math::radians(0.0f)});
ctx->astronomy_system->set_sun_light(sun); ctx->astronomy_system->set_sun_light(sun);
ctx->astronomy_system->set_sky_pass(ctx->overworld_sky_pass); ctx->astronomy_system->set_sky_pass(ctx->overworld_sky_pass);

+ 115
- 0
src/physics/atmosphere.hpp View File

@ -37,12 +37,127 @@ namespace atmosphere {
* @return Particle density at altitude. * @return Particle density at altitude.
* *
* @see https://en.wikipedia.org/wiki/Scale_height * @see https://en.wikipedia.org/wiki/Scale_height
* @see https://en.wikipedia.org/wiki/Barometric_formula
*/ */
template <class T>
T density(T d0, T z, T sh) T density(T d0, T z, T sh)
{ {
return d0 * std::exp(-z / sh); return d0 * std::exp(-z / sh);
} }
/**
* Calculates a particle polarization factor used in computing scattering coefficients.
*
* @param ior Atmospheric index of refraction at sea level.
* @param density Molecular density at sea level.
* @return Polarization factor.
*
* @see Elek, Oskar. (2009). Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time.
*/
template <class T>
T polarization(T ior, T density)
{
const T ior2 = ior * ior;
const T num = T(2) * math::pi<T> * math::pi<T> * ((ior2 - T(1.0)) * (ior2 - T(1.0)));
const T den = T(3) * density * density;
return num / den;
}
/**
* Calculates a Rayleigh scattering coefficient at sea level (wavelength-dependent).
*
* @param wavelength Wavelength of light, in meters.
* @param density Molecular density of Rayleigh particles at sea level.
* @param polarization Rayleigh particle polarization factor.
*
* @see atmosphere::polarization
*
* @see Elek, Oskar. (2009). Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time.
*/
template <class T>
T scatter_rayleigh(T wavelength, T density, T polarization)
{
const T wavelength2 = wavelength * wavelength;
return T(4) * math::pi<T> * density / (wavelength2 * wavelength2) * polarization;
}
/**
* Calculates a Mie scattering coefficient at sea level (wavelength-independent).
*
* @param density Molecular density of Mie particles at sea level.
* @param polarization Mie particle polarization factor.
*
* @see atmosphere::polarization
*
* @see Elek, Oskar. (2009). Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time.
*/
template <class T>
T scatter_mie(T density, T polarization)
{
return T(4) * math::pi<T> * density * polarization;
}
/**
* Calculates an extinction coefficient given a scattering coefficient and an absorbtion coefficient.
*
* @param s Scattering coefficient.
* @param a Absorbtion coefficient.
* @return Extinction coefficient.
*/
template <class T>
T extinction(T s, T a)
{
return s + a;
}
/**
* Calculates the single-scattering albedo (SSA) given a scattering coefficient and an extinction coefficient.
*
* @param s Scattering coefficient.
* @param e Extinction coefficient.
* @return Single-scattering albedo.
*/
template <class T>
T albedo(T s, T e)
{
return s / t;
}
/**
* Approximates the optical depth of exponentially-distributed atmospheric particles between two points using the trapezoidal rule.
*
* @param a Start point.
* @param b End point.
* @param r Radius of the planet.
* @param sh Scale height of the atmospheric particles.
* @param n Number of samples.
* @return Optical depth between @p a and @p b.
*/
template <class T>
T optical_depth(const math::vector3<T>& a, const math::vector3<T>& b, T r, T sh, std::size_t n)
{
sh = T(-1) / sh;
const T h = math::length(b - a) / T(n);
math::vector3<T> dy = (b - a) / T(n);
math::vector3<T> y = a + dy;
T f_x = std::exp((math::length(a) - r) * sh);
T f_y = std::exp((math::length(y) - r) * sh);
T sum = (f_x + f_y);
for (std::size_t i = 1; i < n; ++i)
{
f_x = f_y;
y += dy;
f_y = std::exp((math::length(y) - r) * sh);
sum += (f_x + f_y);
}
return sum / T(2) * h;
}
} // namespace atmosphere } // namespace atmosphere
} // namespace physics } // namespace physics

+ 19
- 0
src/physics/light/phase.hpp View File

@ -29,6 +29,15 @@ namespace light {
/// Light-scattering phase functions. /// Light-scattering phase functions.
namespace phase { namespace phase {
/**
* Cornette-Shanks phase function.
*
* @param mu Cosine of the angle between the light and view directions.
* @param g Asymmetry factor, on [-1, 1]. Positive values cause forward scattering, negative values cause back scattering.
*/
template <class T>
T cornette_shanks(T mu, T g);
/** /**
* HenyeyGreenstein phase function. * HenyeyGreenstein phase function.
* *
@ -57,6 +66,16 @@ constexpr T isotropic()
template <class T> template <class T>
T rayleigh(T mu); T rayleigh(T mu);
template <class T>
T cornette_shanks(T mu, T g)
{
const T k = T(3) / (T(8) * math::pi<T>);
const T gg = g * g;
const T num = (T(1) - gg) * (T(1) + mu * mu);
const T den = (T(2) + gg) * std::pow(T(1) + gg - T(2) * g * mu, T(1.5));
return k * num / den;
}
template <class T> template <class T>
T henyey_greenstein(T mu, T g) T henyey_greenstein(T mu, T g)
{ {

+ 70
- 128
src/renderer/passes/sky-pass.cpp View File

@ -60,19 +60,12 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe
moon_material(nullptr), moon_material(nullptr),
moon_model_vao(nullptr), moon_model_vao(nullptr),
moon_shader_program(nullptr), moon_shader_program(nullptr),
blue_noise_map(nullptr),
sky_gradient(nullptr),
sky_gradient2(nullptr),
observer_location{0.0f, 0.0f, 0.0f},
time_tween(nullptr), time_tween(nullptr),
time_of_day_tween(0.0, math::lerp<float, float>),
julian_day_tween(0.0, math::lerp<float, float>),
horizon_color_tween(float3{0.0f, 0.0f, 0.0f}, math::lerp<float3, float>),
zenith_color_tween(float3{1.0f, 1.0f, 1.0f}, math::lerp<float3, float>),
observer_altitude_tween(0.0f, math::lerp<float, float>),
sun_position_tween(float3{1.0f, 0.0f, 0.0f}, math::lerp<float3, float>),
sun_color_tween(float3{1.0f, 1.0f, 1.0f}, math::lerp<float3, float>), sun_color_tween(float3{1.0f, 1.0f, 1.0f}, math::lerp<float3, float>),
topocentric_frame_translation({0, 0, 0}, math::lerp<float3, float>), topocentric_frame_translation({0, 0, 0}, math::lerp<float3, float>),
topocentric_frame_rotation(math::quaternion<float>::identity(), math::nlerp<float>),
sun_object(nullptr)
topocentric_frame_rotation(math::quaternion<float>::identity(), math::nlerp<float>)
{ {
// Load star catalog // Load star catalog
string_table* star_catalog = resource_manager->load<string_table>("stars.csv"); string_table* star_catalog = resource_manager->load<string_table>("stars.csv");
@ -105,7 +98,9 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe
bv_color = std::stod(catalog_row[4]); bv_color = std::stod(catalog_row[4]);
} }
catch (const std::exception& e) catch (const std::exception& e)
{}
{
continue;
}
// Convert right ascension and declination from degrees to radians // Convert right ascension and declination from degrees to radians
ra = math::wrap_radians(math::radians(ra)); ra = math::wrap_radians(math::radians(ra));
@ -202,12 +197,8 @@ void sky_pass::render(render_context* context) const
float4x4 model_view_projection = projection * model_view; float4x4 model_view_projection = projection * model_view;
float exposure = std::exp2(camera.get_exposure_tween().interpolate(context->alpha)); float exposure = std::exp2(camera.get_exposure_tween().interpolate(context->alpha));
float time_of_day = time_of_day_tween.interpolate(context->alpha);
float julian_day = julian_day_tween.interpolate(context->alpha);
float3 horizon_color = horizon_color_tween.interpolate(context->alpha);
float3 zenith_color = zenith_color_tween.interpolate(context->alpha);
float3 sun_color = sun_color_tween.interpolate(context->alpha);
// Interpolate observer altitude
float observer_altitude = observer_altitude_tween.interpolate(context->alpha);
// Construct tweened inertial to topocentric frame // Construct tweened inertial to topocentric frame
physics::frame<float> topocentric_frame = physics::frame<float> topocentric_frame =
@ -216,15 +207,12 @@ void sky_pass::render(render_context* context) const
topocentric_frame_rotation.interpolate(context->alpha) topocentric_frame_rotation.interpolate(context->alpha)
}; };
// Get topocentric space sun position
float3 sun_position = {0, 0, 0};
if (sun_object != nullptr)
{
sun_position = math::normalize(sun_object->get_transform_tween().interpolate(context->alpha).translation);
}
// Get topocentric space direction to sun
float3 sun_position = sun_position_tween.interpolate(context->alpha);
float3 sun_direction = math::normalize(sun_position);
// Get topocentric space moon position
float3 moon_position = {0, 0, 0};
// Interpolate sun color
float3 sun_color = sun_color_tween.interpolate(context->alpha);
// Draw sky model // Draw sky model
{ {
@ -233,41 +221,34 @@ void sky_pass::render(render_context* context) const
// Upload shader parameters // Upload shader parameters
if (model_view_projection_input) if (model_view_projection_input)
model_view_projection_input->upload(model_view_projection); model_view_projection_input->upload(model_view_projection);
if (horizon_color_input)
horizon_color_input->upload(horizon_color);
if (zenith_color_input)
zenith_color_input->upload(zenith_color);
if (sun_color_input)
sun_color_input->upload(sun_color);
if (mouse_input) if (mouse_input)
mouse_input->upload(mouse_position); mouse_input->upload(mouse_position);
if (resolution_input) if (resolution_input)
resolution_input->upload(resolution); resolution_input->upload(resolution);
if (time_input) if (time_input)
time_input->upload(time); time_input->upload(time);
if (time_of_day_input)
time_of_day_input->upload(time_of_day);
if (blue_noise_map_input)
blue_noise_map_input->upload(blue_noise_map);
if (sky_gradient_input && sky_gradient)
sky_gradient_input->upload(sky_gradient);
if (sky_gradient2_input && sky_gradient2)
sky_gradient2_input->upload(sky_gradient2);
if (observer_location_input)
observer_location_input->upload(observer_location);
if (sun_position_input)
sun_position_input->upload(sun_position);
if (moon_position_input)
moon_position_input->upload(moon_position);
if (julian_day_input)
julian_day_input->upload(julian_day);
if (cos_moon_angular_radius_input)
cos_moon_angular_radius_input->upload(cos_moon_angular_radius);
if (cos_sun_angular_radius_input)
cos_sun_angular_radius_input->upload(cos_sun_angular_radius);
if (exposure_input) if (exposure_input)
exposure_input->upload(exposure); exposure_input->upload(exposure);
if (observer_altitude_input)
observer_altitude_input->upload(observer_altitude);
if (sun_direction_input)
sun_direction_input->upload(sun_direction);
if (cos_sun_angular_radius_input)
cos_sun_angular_radius_input->upload(cos_sun_angular_radius);
if (sun_color_input)
sun_color_input->upload(sun_color);
if (scale_height_rm_input)
scale_height_rm_input->upload(scale_height_rm);
if (rayleigh_scattering_input)
rayleigh_scattering_input->upload(rayleigh_scattering);
if (mie_scattering_input)
mie_scattering_input->upload(mie_scattering);
if (mie_asymmetry_input)
mie_asymmetry_input->upload(mie_asymmetry);
if (atmosphere_radii_input)
atmosphere_radii_input->upload(atmosphere_radii);
sky_material->upload(context->alpha); sky_material->upload(context->alpha);
rasterizer->draw_arrays(*sky_model_vao, sky_model_drawing_mode, sky_model_start_index, sky_model_index_count); rasterizer->draw_arrays(*sky_model_vao, sky_model_drawing_mode, sky_model_start_index, sky_model_index_count);
@ -278,6 +259,8 @@ void sky_pass::render(render_context* context) const
glBlendFunc(GL_ONE, GL_ONE); glBlendFunc(GL_ONE, GL_ONE);
// Draw moon model // Draw moon model
/*
float3 moon_position = {0, 0, 0};
if (moon_position.y >= -moon_angular_radius) if (moon_position.y >= -moon_angular_radius)
{ {
@ -307,34 +290,13 @@ void sky_pass::render(render_context* context) const
moon_material->upload(context->alpha); moon_material->upload(context->alpha);
rasterizer->draw_arrays(*moon_model_vao, moon_model_drawing_mode, moon_model_start_index, moon_model_index_count); rasterizer->draw_arrays(*moon_model_vao, moon_model_drawing_mode, moon_model_start_index, moon_model_index_count);
} }
*/
// Draw stars // Draw stars
{ {
float star_distance = (clip_near + clip_far) * 0.5f; float star_distance = (clip_near + clip_far) * 0.5f;
double lat = math::radians(1.0);
double lst = time_of_day / 24.0f * math::two_pi<float>;
//std::cout << "lst: " << lst << std::endl;
/*
double3x3 equatorial_to_horizontal = coordinates::rectangular::equatorial::to_horizontal(lat, lst);
const double3x3 horizontal_to_local = coordinates::rectangular::rotate_x(-math::half_pi<double>) * coordinates::rectangular::rotate_z(-math::half_pi<double>);
double3x3 rotation = horizontal_to_local * equatorial_to_horizontal;
model = math::type_cast<float>(math::scale(math::resize<4, 4>(rotation), double3{star_distance, star_distance, star_distance}));;
*/
//math::transform<float> star_transform;
//star_transform.translation = {0.0, 0.0, 0.0};
//star_transform.rotation = math::normalize(rotation_x * rotation_y);
//star_transform.rotation = math::normalize(math::type_cast<float>(math::quaternion_cast(rotation)));
//star_transform.rotation = math::identity_quaternion<float>;
//star_transform.scale = {star_distance, star_distance, star_distance};
//model = math::matrix_cast(star_transform);
model = topocentric_frame.matrix();
model = math::resize<4, 4>(math::matrix_cast<float>(topocentric_frame.rotation));
model = math::scale(model, {star_distance, star_distance, star_distance}); model = math::scale(model, {star_distance, star_distance, star_distance});
model_view = view * model; model_view = view * model;
@ -377,23 +339,20 @@ void sky_pass::set_sky_model(const model* model)
if (sky_shader_program) if (sky_shader_program)
{ {
model_view_projection_input = sky_shader_program->get_input("model_view_projection"); model_view_projection_input = sky_shader_program->get_input("model_view_projection");
horizon_color_input = sky_shader_program->get_input("horizon_color");
zenith_color_input = sky_shader_program->get_input("zenith_color");
sun_color_input = sky_shader_program->get_input("sun_color");
mouse_input = sky_shader_program->get_input("mouse"); mouse_input = sky_shader_program->get_input("mouse");
resolution_input = sky_shader_program->get_input("resolution"); resolution_input = sky_shader_program->get_input("resolution");
time_input = sky_shader_program->get_input("time"); time_input = sky_shader_program->get_input("time");
time_of_day_input = sky_shader_program->get_input("time_of_day");
blue_noise_map_input = sky_shader_program->get_input("blue_noise_map");
sky_gradient_input = sky_shader_program->get_input("sky_gradient");
sky_gradient2_input = sky_shader_program->get_input("sky_gradient2");
observer_location_input = sky_shader_program->get_input("observer_location");
sun_position_input = sky_shader_program->get_input("sun_position");
moon_position_input = sky_shader_program->get_input("moon_position");
julian_day_input = sky_shader_program->get_input("julian_day");
cos_moon_angular_radius_input = sky_shader_program->get_input("cos_moon_angular_radius");
cos_sun_angular_radius_input = sky_shader_program->get_input("cos_sun_angular_radius");
exposure_input = sky_shader_program->get_input("camera.exposure"); exposure_input = sky_shader_program->get_input("camera.exposure");
observer_altitude_input = sky_shader_program->get_input("observer_altitude");
sun_direction_input = sky_shader_program->get_input("sun_direction");
sun_color_input = sky_shader_program->get_input("sun_color");
cos_sun_angular_radius_input = sky_shader_program->get_input("cos_sun_angular_radius");
scale_height_rm_input = sky_shader_program->get_input("scale_height_rm");
rayleigh_scattering_input = sky_shader_program->get_input("rayleigh_scattering");
mie_scattering_input = sky_shader_program->get_input("mie_scattering");
mie_asymmetry_input = sky_shader_program->get_input("mie_asymmetry");
atmosphere_radii_input = sky_shader_program->get_input("atmosphere_radii");
} }
} }
} }
@ -441,18 +400,11 @@ void sky_pass::set_moon_model(const model* model)
void sky_pass::update_tweens() void sky_pass::update_tweens()
{ {
julian_day_tween.update();
time_of_day_tween.update();
horizon_color_tween.update();
zenith_color_tween.update();
observer_altitude_tween.update();
sun_position_tween.update();
sun_color_tween.update();
topocentric_frame_translation.update(); topocentric_frame_translation.update();
topocentric_frame_rotation.update(); topocentric_frame_rotation.update();
sun_color_tween.update();
}
void sky_pass::set_time_of_day(float time)
{
time_of_day_tween[1] = time;
} }
void sky_pass::set_time_tween(const tween<double>* time) void sky_pass::set_time_tween(const tween<double>* time)
@ -460,63 +412,53 @@ void sky_pass::set_time_tween(const tween* time)
this->time_tween = time; this->time_tween = time;
} }
void sky_pass::set_blue_noise_map(const gl::texture_2d* texture)
{
blue_noise_map = texture;
}
void sky_pass::set_sky_gradient(const gl::texture_2d* texture, const gl::texture_2d* texture2)
{
sky_gradient = texture;
sky_gradient2 = texture2;
}
void sky_pass::set_julian_day(float jd)
void sky_pass::set_topocentric_frame(const physics::frame<float>& frame)
{ {
julian_day_tween[1] = jd;
topocentric_frame_translation[1] = frame.translation;
topocentric_frame_rotation[1] = frame.rotation;
} }
void sky_pass::set_observer_location(float altitude, float latitude, float longitude)
void sky_pass::set_sun_position(const float3& position)
{ {
observer_location = {altitude, latitude, longitude};
sun_position_tween[1] = position;
} }
void sky_pass::set_moon_angular_radius(float radius)
void sky_pass::set_sun_color(const float3& color)
{ {
moon_angular_radius = radius;
cos_moon_angular_radius = std::cos(moon_angular_radius);
sun_color_tween[1] = color;
} }
void sky_pass::set_sun_angular_radius(float radius) void sky_pass::set_sun_angular_radius(float radius)
{ {
sun_angular_radius = radius;
cos_sun_angular_radius = std::cos(sun_angular_radius);
cos_sun_angular_radius = std::cos(radius);
} }
void sky_pass::set_topocentric_frame(const physics::frame<float>& frame)
void sky_pass::set_observer_altitude(float altitude)
{ {
topocentric_frame_translation[1] = frame.translation;
topocentric_frame_rotation[1] = frame.rotation;
observer_altitude_tween[1] = altitude;
} }
void sky_pass::set_sun_object(const scene::object_base* object)
void sky_pass::set_scale_heights(float rayleigh, float mie)
{ {
sun_object = object;
scale_height_rm = {rayleigh, mie};
} }
void sky_pass::set_horizon_color(const float3& color)
void sky_pass::set_scattering_coefficients(const float3& r, const float3& m)
{ {
horizon_color_tween[1] = color;
rayleigh_scattering = r;
mie_scattering = m;
} }
void sky_pass::set_zenith_color(const float3& color)
void sky_pass::set_mie_asymmetry(float g)
{ {
zenith_color_tween[1] = color;
mie_asymmetry = {g, g * g};
} }
void sky_pass::set_sun_color(const float3& color)
void sky_pass::set_atmosphere_radii(float inner, float outer)
{ {
sun_color_tween[1] = color;
atmosphere_radii.x = inner;
atmosphere_radii.y = outer;
atmosphere_radii.z = outer * outer;
} }
void sky_pass::handle_event(const mouse_moved_event& event) void sky_pass::handle_event(const mouse_moved_event& event)

+ 24
- 36
src/renderer/passes/sky-pass.hpp View File

@ -53,46 +53,39 @@ public:
void update_tweens(); void update_tweens();
void set_sky_model(const model* model); void set_sky_model(const model* model);
void set_horizon_color(const float3& color);
void set_zenith_color(const float3& color);
void set_sun_color(const float3& color);
void set_time_of_day(float time);
void set_blue_noise_map(const gl::texture_2d* texture);
void set_sky_gradient(const gl::texture_2d* texture, const gl::texture_2d* texture2);
void set_time_tween(const tween<double>* time); void set_time_tween(const tween<double>* time);
void set_moon_model(const model* model); void set_moon_model(const model* model);
void set_julian_day(float jd);
void set_observer_location(float altitude, float latitude, float longitude);
void set_topocentric_frame(const physics::frame<float>& frame);
void set_moon_angular_radius(float radius);
void set_sun_position(const float3& position);
void set_sun_color(const float3& color);
void set_sun_angular_radius(float radius); void set_sun_angular_radius(float radius);
void set_topocentric_frame(const physics::frame<float>& frame);
void set_sun_object(const scene::object_base* object);
void set_observer_altitude(float altitude);
void set_scale_heights(float rayleigh, float mie);
void set_scattering_coefficients(const float3& r, const float3& m);
void set_mie_asymmetry(float g);
void set_atmosphere_radii(float inner, float outer);
private: private:
virtual void handle_event(const mouse_moved_event& event); virtual void handle_event(const mouse_moved_event& event);
gl::shader_program* sky_shader_program; gl::shader_program* sky_shader_program;
const gl::shader_input* model_view_projection_input; const gl::shader_input* model_view_projection_input;
const gl::shader_input* horizon_color_input;
const gl::shader_input* zenith_color_input;
const gl::shader_input* mouse_input; const gl::shader_input* mouse_input;
const gl::shader_input* resolution_input; const gl::shader_input* resolution_input;
const gl::shader_input* time_input; const gl::shader_input* time_input;
const gl::shader_input* time_of_day_input;
const gl::shader_input* observer_location_input;
const gl::shader_input* sun_position_input;
const gl::shader_input* moon_position_input;
const gl::shader_input* blue_noise_map_input;
const gl::shader_input* julian_day_input;
const gl::shader_input* cos_sun_angular_radius_input;
const gl::shader_input* cos_moon_angular_radius_input;
const gl::shader_input* sky_gradient_input;
const gl::shader_input* sky_gradient2_input;
const gl::shader_input* exposure_input; const gl::shader_input* exposure_input;
const gl::shader_input* observer_altitude_input;
const gl::shader_input* sun_direction_input;
const gl::shader_input* sun_color_input; const gl::shader_input* sun_color_input;
const gl::shader_input* cos_sun_angular_radius_input;
const gl::shader_input* scale_height_rm_input;
const gl::shader_input* rayleigh_scattering_input;
const gl::shader_input* mie_scattering_input;
const gl::shader_input* mie_asymmetry_input;
const gl::shader_input* atmosphere_radii_input;
gl::shader_program* moon_shader_program; gl::shader_program* moon_shader_program;
const gl::shader_input* moon_model_view_projection_input; const gl::shader_input* moon_model_view_projection_input;
@ -127,25 +120,20 @@ private:
const gl::texture_2d* sky_gradient; const gl::texture_2d* sky_gradient;
const gl::texture_2d* sky_gradient2; const gl::texture_2d* sky_gradient2;
float2 mouse_position; float2 mouse_position;
float3 observer_location;
const tween<double>* time_tween; const tween<double>* time_tween;
tween<float> time_of_day_tween;
tween<float> julian_day_tween;
tween<float3> horizon_color_tween;
tween<float3> zenith_color_tween;
tween<float> observer_altitude_tween;
tween<float3> sun_position_tween;
tween<float3> sun_color_tween; tween<float3> sun_color_tween;
tween<float3> topocentric_frame_translation; tween<float3> topocentric_frame_translation;
tween<math::quaternion<float>> topocentric_frame_rotation; tween<math::quaternion<float>> topocentric_frame_rotation;
float moon_angular_radius;
float cos_moon_angular_radius;
float sun_angular_radius;
float cos_sun_angular_radius; float cos_sun_angular_radius;
const scene::object_base* sun_object;
float2 scale_height_rm;
float3 rayleigh_scattering;
float3 mie_scattering;
float2 mie_asymmetry;
float3 atmosphere_radii;
}; };
#endif // ANTKEEPER_SKY_PASS_HPP #endif // ANTKEEPER_SKY_PASS_HPP

Loading…
Cancel
Save