17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 03:59:19 +02:00
Files
maszyna/shaders/atmosphere_integrate.glsl

106 lines
4.1 KiB
GLSL

// scattering along a ray through the atmosphere, see atmosphere_common.glsl. the includer declares the samplers
// atmo_transmittance_lut and (when multiple scattering is used) atmo_multiscattering_lut
// above the ground by this much (km), so the test for the shadow of the planet does not hit the point itself
const float ATMO_PLANET_RADIUS_OFFSET = 0.01;
struct atmo_scattering_s
{
// light scattered towards the origin
vec3 luminance;
// transmittance from the origin to the end of the ray
vec3 transmittance;
// light scattered once more, as if the light arriving at every point was 1 (multiple scattering table)
vec3 multiscattering_as_1;
};
vec3 atmo_transmittance_to_top(float radius, float cossunzenith)
{
return texture(atmo_transmittance_lut, atmo_transmittance_uv(radius, cossunzenith)).rgb;
}
// Origin and Direction (unit) in the frame of the planet. with Ground the light the ground reflects at the end of the ray
// is included. Physicalphase: rayleigh and mie phase functions, otherwise isotropic (multiple scattering table).
// Multiscattering: light scattered more than once comes from the multiple scattering table
atmo_scattering_s atmo_integrate(vec3 origin, vec3 direction, vec3 sundirection, bool ground, int samples, bool physicalphase, bool multiscattering)
{
atmo_scattering_s result;
result.luminance = vec3(0.0);
result.transmittance = vec3(1.0);
result.multiscattering_as_1 = vec3(0.0);
float tbottom = atmo_ray_sphere_nearest(origin, direction, atmo_bottom_radius);
float ttop = atmo_ray_sphere_nearest(origin, direction, atmo_top_radius);
float tmax;
if (tbottom < 0.0)
{
if (ttop < 0.0)
return result;
tmax = ttop;
}
else
{
tmax = (ttop > 0.0) ? min(ttop, tbottom) : tbottom;
}
float cosangle = dot(direction, sundirection);
const float uniformphase = 1.0 / (4.0 * ATMO_PI);
float miephase = physicalphase ? atmo_mie_phase(cosangle) : uniformphase;
float rayleighphase = physicalphase ? atmo_rayleigh_phase(cosangle) : uniformphase;
vec3 throughput = vec3(1.0);
float invsamples = 1.0 / float(samples);
for (int s = 0; s < samples; ++s)
{
// the steps grow with the distance: the medium near the observer matters most
float t0 = float(s) * invsamples;
float t1 = float(s + 1) * invsamples;
t0 = t0 * t0 * tmax;
t1 = t1 * t1 * tmax;
float dt = t1 - t0;
float t = t0 + dt * 0.3;
vec3 position = origin + t * direction;
float radius = length(position);
vec3 up = position / radius;
atmo_medium_s medium = atmo_sample_medium(radius);
vec3 extinction = max(medium.extinction, vec3(1e-7));
vec3 sampletransmittance = exp(-extinction * dt);
float cossunzenith = dot(sundirection, up);
vec3 suntransmittance = atmo_transmittance_to_top(radius, cossunzenith);
float earthshadow = (atmo_ray_sphere_nearest(position + up * ATMO_PLANET_RADIUS_OFFSET, sundirection, atmo_bottom_radius) >= 0.0) ? 0.0 : 1.0;
vec3 multiscatteredluminance = vec3(0.0);
#ifdef ATMO_MULTISCATTERING_LUT
if (multiscattering)
multiscatteredluminance = texture(atmo_multiscattering_lut, atmo_multiscattering_uv(radius, cossunzenith)).rgb;
#endif
vec3 phasescattering = medium.mie_scattering * miephase + medium.rayleigh_scattering * rayleighphase;
vec3 S = earthshadow * suntransmittance * phasescattering + multiscatteredluminance * medium.scattering;
// analytic integral over the step of the light scattered at the sample, attenuated on the way to its start
vec3 Sint = (S - S * sampletransmittance) / extinction;
result.luminance += throughput * Sint;
vec3 MSint = (medium.scattering - medium.scattering * sampletransmittance) / extinction;
result.multiscattering_as_1 += throughput * MSint;
throughput *= sampletransmittance;
}
if (ground && tbottom > 0.0 && tmax == tbottom)
{
vec3 position = origin + tbottom * direction;
float radius = length(position);
vec3 up = position / radius;
float cossunzenith = dot(sundirection, up);
vec3 suntransmittance = atmo_transmittance_to_top(radius, cossunzenith);
float NdotL = clamp(dot(up, sundirection), 0.0, 1.0);
result.luminance += suntransmittance * throughput * NdotL * atmo_ground.rgb / ATMO_PI;
}
result.transmittance = throughput;
return result;
}