152 lines
6.1 KiB
GLSL
152 lines
6.1 KiB
GLSL
#version 330 core
|
|
|
|
uniform bool sun_disk = true;
|
|
uniform vec3 beta_rayleigh = vec3(5.5e-6, 13.0e-6, 22.4e-6);
|
|
uniform float beta_mie = 21e-6;
|
|
uniform vec3 beta_absortion = vec3(2.04e-5, 4.97e-5, 1.95e-6);
|
|
uniform float beta_ambient = 0.0;
|
|
uniform float rayleigh_scale_height = 8e3;
|
|
uniform float mie_scale_height = 1.2e3;
|
|
uniform float absortion_scale_height = 30e3;
|
|
uniform float absortion_falloff = 3e3;
|
|
uniform int num_samples = 64;
|
|
uniform int num_light_samples = 4;
|
|
|
|
const float PI = 3.141592653;
|
|
const float ATMOSPHERE_RADIUS = 6471e3;
|
|
const vec3 SUN_INTENSITY = vec3(20.0);
|
|
const float COS_SUN_ANGULAR_DIAMETER = 0.999956676946448443553574619906976478926848692873900859324;
|
|
|
|
vec2 raySphereIntersection(vec3 r0, vec3 rd, float radius)
|
|
{
|
|
float a = dot(rd, rd);
|
|
float b = 2.0 * dot(rd, r0);
|
|
float c = dot(r0, r0) - (radius * radius);
|
|
float d = (b*b) - 4.0*a*c;
|
|
if (d < 0.0) return vec2(1e5, -1e5);
|
|
return vec2((-b - sqrt(d))/(2.0*a), (-b + sqrt(d))/(2.0*a));
|
|
}
|
|
|
|
vec3 calculateScattering(vec3 rayOrigin,
|
|
vec3 rayDir,
|
|
vec3 sceneColor,
|
|
float depth,
|
|
float maxDist,
|
|
float earthRadius,
|
|
vec3 lightDir)
|
|
{
|
|
vec2 hit = raySphereIntersection(rayOrigin, rayDir, ATMOSPHERE_RADIUS);
|
|
if (hit.x > hit.y) {
|
|
// The ray did not hit the atmosphere, we are in outer space
|
|
return sceneColor;
|
|
}
|
|
hit.x = max(hit.x, 0.0); // Do not sample behind the camera
|
|
// Avoid clamping the ray to the far plane when there is no geometry in
|
|
// front of the sky
|
|
if (depth < 1.0) {
|
|
// Stop the ray at the geometry
|
|
hit.y = min(hit.y, maxDist);
|
|
} else {
|
|
// If there is no geometry, simulate a collision with the Earth at sea
|
|
// level. This only happens when FG hasn't loaded any terrain
|
|
vec2 hitEarth = raySphereIntersection(
|
|
rayOrigin, rayDir, earthRadius - 1.0);
|
|
if (hitEarth.x < hitEarth.y && hitEarth.x > 0.0) {
|
|
hit.y = min(hit.y, hitEarth.x);
|
|
}
|
|
}
|
|
|
|
float stepSize = (hit.y - hit.x) / float(num_samples);
|
|
|
|
const float g = 0.758; // Mie scattering direction
|
|
const float gg = g*g;
|
|
float mu = dot(rayDir, lightDir);
|
|
float mumu = mu*mu;
|
|
float phaseRayleigh = 3.0 / (50.2654824574 /* 16*PI */) * (1.0 + mumu);
|
|
float phaseMie = 3.0 / (25.1327412287 /* 8*PI */) * ((1.0 - gg) * (mumu + 1.0)) /
|
|
(pow(1.0 + gg - 2.0 * mu * g, 1.5) * (2.0 + gg));
|
|
|
|
float opticalDepthRayleigh = 0.0;
|
|
float opticalDepthMie = 0.0;
|
|
float opticalDepthAbsortion = 0.0;
|
|
|
|
float primaryTime = hit.x;
|
|
|
|
vec3 extinctionFactor = vec3(0.0);
|
|
vec3 totalRayleigh = vec3(0.0);
|
|
vec3 totalMie = vec3(0.0);
|
|
|
|
for (int i = 0; i < num_samples; ++i) {
|
|
vec3 samplePoint = rayOrigin + rayDir * (primaryTime + stepSize * 0.5);
|
|
|
|
float altitude = length(samplePoint) - earthRadius;
|
|
|
|
float densityRayleigh = exp(-altitude / rayleigh_scale_height);
|
|
float densityMie = exp(-altitude / mie_scale_height);
|
|
float densityAbsortion = clamp((1.0 / cosh((absortion_scale_height - altitude) / absortion_falloff)) * densityRayleigh, 0.0, 1.0);
|
|
float stepOpticalDepthRayleigh = densityRayleigh * stepSize;
|
|
float stepOpticalDepthMie = densityMie * stepSize;
|
|
float stepOpticalDepthAbsortion = densityAbsortion * stepSize;
|
|
opticalDepthRayleigh += stepOpticalDepthRayleigh;
|
|
opticalDepthMie += stepOpticalDepthMie;
|
|
opticalDepthAbsortion += stepOpticalDepthAbsortion;
|
|
|
|
vec2 pl = raySphereIntersection(samplePoint, lightDir, ATMOSPHERE_RADIUS);
|
|
float stepSizeLight = pl.y / float(num_light_samples);
|
|
|
|
float opticalDepthLightRayleigh = 0.0;
|
|
float opticalDepthLightMie = 0.0;
|
|
float opticalDepthLightAbsortion = 0.0;
|
|
|
|
float secondaryTime = 0.0;
|
|
|
|
for (int j = 0; j < num_light_samples; ++j) {
|
|
vec3 samplePointLight = samplePoint + lightDir *
|
|
(secondaryTime + stepSizeLight * 0.5);
|
|
|
|
float altitudeLight = length(samplePointLight) - earthRadius;
|
|
|
|
float densityLightRayleigh = exp(-altitudeLight / rayleigh_scale_height);
|
|
float densityLightMie = exp(-altitudeLight / mie_scale_height);
|
|
float densityLightAbsortion = clamp((1.0 / cosh((absortion_scale_height - altitudeLight) / absortion_falloff)) * densityLightRayleigh, 0.0, 1.0);
|
|
opticalDepthLightRayleigh += densityLightRayleigh * stepSizeLight;
|
|
opticalDepthLightMie += densityLightMie * stepSizeLight;
|
|
opticalDepthLightAbsortion += densityLightAbsortion * stepSizeLight;
|
|
|
|
secondaryTime += stepSizeLight;
|
|
}
|
|
|
|
vec3 tau =
|
|
beta_rayleigh * (opticalDepthRayleigh + opticalDepthLightRayleigh) +
|
|
beta_mie * (opticalDepthMie + opticalDepthLightMie) +
|
|
beta_absortion * (opticalDepthAbsortion + opticalDepthLightAbsortion);
|
|
vec3 attenuation = exp(-tau);
|
|
|
|
extinctionFactor += attenuation;
|
|
|
|
totalRayleigh += stepOpticalDepthRayleigh * attenuation;
|
|
totalMie += stepOpticalDepthMie * attenuation;
|
|
|
|
primaryTime += stepSize;
|
|
}
|
|
|
|
vec3 opacity = exp(-(beta_rayleigh * opticalDepthRayleigh +
|
|
beta_mie * opticalDepthMie +
|
|
beta_absortion * opticalDepthAbsortion));
|
|
|
|
vec3 color = SUN_INTENSITY *
|
|
(totalRayleigh * beta_rayleigh * phaseRayleigh +
|
|
totalMie * beta_mie * phaseMie +
|
|
opticalDepthRayleigh * beta_ambient)
|
|
+ sceneColor * opacity;
|
|
|
|
if (sun_disk && (depth >= 1.0)) {
|
|
float costheta = dot(rayDir, lightDir);
|
|
float sundisk = smoothstep(COS_SUN_ANGULAR_DIAMETER,
|
|
COS_SUN_ANGULAR_DIAMETER + 0.00002,
|
|
costheta);
|
|
color += SUN_INTENSITY * extinctionFactor * sundisk;
|
|
}
|
|
|
|
return color;
|
|
}
|