2021-07-28 07:40:04 +00:00
|
|
|
// An implementation of Sébastien Hillaire's "A Scalable and Production Ready
|
|
|
|
// Sky and Atmosphere Rendering Technique".
|
|
|
|
//
|
|
|
|
// This shader generates the sky-view texture. Since the sky generally has low
|
|
|
|
// frequency detail, it's possible to pre-compute it on a small texture and
|
|
|
|
// sample it later when rendering the skydome. This effectively bypasses the
|
|
|
|
// need for raymarching on screen-sized textures, which is specially costly on
|
|
|
|
// larger resolutions like 4K.
|
|
|
|
|
2021-07-26 10:10:58 +00:00
|
|
|
#version 330 core
|
|
|
|
|
|
|
|
out vec3 fragColor;
|
|
|
|
|
|
|
|
in vec2 texCoord;
|
|
|
|
|
2021-07-28 07:40:04 +00:00
|
|
|
uniform sampler2D transmittance_lut;
|
|
|
|
uniform sampler2D multiscattering_lut;
|
|
|
|
|
2021-08-19 10:50:01 +00:00
|
|
|
uniform float fg_SunZenithCosTheta;
|
|
|
|
uniform float fg_CameraDistanceToEarthCenter;
|
|
|
|
uniform float fg_EarthRadius;
|
|
|
|
|
2021-07-26 10:10:58 +00:00
|
|
|
const float PI = 3.141592653;
|
|
|
|
|
2021-07-28 07:40:04 +00:00
|
|
|
const float ATMOSPHERE_RADIUS = 6471e3;
|
|
|
|
const int SCATTERING_SAMPLES = 32;
|
|
|
|
|
|
|
|
float raySphereIntersection(vec3 ro, vec3 rd, float radius);
|
|
|
|
vec3 sampleMedium(in float height,
|
|
|
|
out float mieScattering, out float mieAbsorption,
|
|
|
|
out vec3 rayleighScattering, out vec3 ozoneAbsorption);
|
|
|
|
float miePhaseFunction(float cosTheta);
|
|
|
|
float rayleighPhaseFunction(float cosTheta);
|
|
|
|
vec3 getValueFromLUT(sampler2D lut, float sunCosTheta, float normalizedHeight);
|
2021-07-26 10:10:58 +00:00
|
|
|
|
|
|
|
void main()
|
|
|
|
{
|
|
|
|
// Always leave the sun right in the middle of the texture as the skydome
|
|
|
|
// model is already being rotated.
|
2021-08-19 10:50:01 +00:00
|
|
|
vec3 sunDir = vec3(-sqrt(1.0 - fg_SunZenithCosTheta*fg_SunZenithCosTheta),
|
|
|
|
0.0,
|
|
|
|
fg_SunZenithCosTheta);
|
2021-07-26 10:10:58 +00:00
|
|
|
|
|
|
|
float azimuth = 2.0 * PI * texCoord.x; // [0, 2pi]
|
|
|
|
// Apply a non-linear transformation to the elevation to dedicate more
|
|
|
|
// texels to the horizon, which is where having more detail matters.
|
|
|
|
float l = texCoord.y * 2.0 - 1.0;
|
|
|
|
float elev = l*l * sign(l) * PI * 0.5; // [-pi/2, pi/2]
|
|
|
|
vec3 rayDir = vec3(cos(elev) * cos(azimuth), cos(elev) * sin(azimuth), sin(elev));
|
|
|
|
|
2021-08-19 10:50:01 +00:00
|
|
|
vec3 rayOrigin = vec3(0.0, 0.0, fg_CameraDistanceToEarthCenter);
|
2021-07-28 07:40:04 +00:00
|
|
|
|
2021-08-19 10:50:01 +00:00
|
|
|
// Handle the camera being underground
|
|
|
|
float earthRadius = min(fg_EarthRadius, fg_CameraDistanceToEarthCenter);
|
2021-07-28 07:40:04 +00:00
|
|
|
|
|
|
|
float atmosDist = raySphereIntersection(rayOrigin, rayDir, ATMOSPHERE_RADIUS);
|
|
|
|
float groundDist = raySphereIntersection(rayOrigin, rayDir, earthRadius);
|
|
|
|
|
|
|
|
float tmax;
|
2021-08-19 10:50:01 +00:00
|
|
|
if (fg_CameraDistanceToEarthCenter < ATMOSPHERE_RADIUS) {
|
2021-07-28 07:40:04 +00:00
|
|
|
// We are inside the atmosphere
|
|
|
|
if (groundDist < 0.0) {
|
|
|
|
// No ground collision, use the distance to the outer atmosphere
|
|
|
|
tmax = atmosDist;
|
|
|
|
} else {
|
|
|
|
// Use the distance to the ground
|
|
|
|
tmax = groundDist;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// We are in outer space, skip
|
|
|
|
fragColor = vec3(0.0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
float cosTheta = dot(rayDir, sunDir);
|
|
|
|
float miePhase = miePhaseFunction(cosTheta);
|
|
|
|
float rayleighPhase = rayleighPhaseFunction(-cosTheta);
|
|
|
|
|
|
|
|
vec3 L = vec3(0.0);
|
|
|
|
vec3 throughput = vec3(1.0);
|
|
|
|
float t = 0.0;
|
|
|
|
|
|
|
|
for (int i = 0; i < SCATTERING_SAMPLES; ++i) {
|
|
|
|
float newT = ((float(i) + 0.3) / SCATTERING_SAMPLES) * tmax;
|
|
|
|
float dt = newT - t;
|
|
|
|
t = newT;
|
|
|
|
|
|
|
|
vec3 samplePos = rayOrigin + rayDir * t;
|
2021-08-19 10:50:01 +00:00
|
|
|
float height = length(samplePos) - fg_EarthRadius;
|
|
|
|
float normalizedHeight = height / (ATMOSPHERE_RADIUS - fg_EarthRadius);
|
2021-07-28 07:40:04 +00:00
|
|
|
|
|
|
|
float mieScattering, mieAbsorption;
|
|
|
|
vec3 rayleighScattering, ozoneAbsorption;
|
|
|
|
vec3 extinction = sampleMedium(height, mieScattering, mieAbsorption,
|
|
|
|
rayleighScattering, ozoneAbsorption);
|
|
|
|
|
|
|
|
vec3 sampleTransmittance = exp(-dt*extinction);
|
|
|
|
|
|
|
|
vec3 sunTransmittance = getValueFromLUT(
|
2021-08-19 10:50:01 +00:00
|
|
|
transmittance_lut, fg_SunZenithCosTheta, normalizedHeight);
|
2021-07-28 07:40:04 +00:00
|
|
|
vec3 multiscattering = getValueFromLUT(
|
2021-08-19 10:50:01 +00:00
|
|
|
multiscattering_lut, fg_SunZenithCosTheta, normalizedHeight);
|
2021-07-28 07:40:04 +00:00
|
|
|
|
|
|
|
vec3 S =
|
|
|
|
rayleighScattering * (rayleighPhase * sunTransmittance + multiscattering) +
|
|
|
|
mieScattering * (miePhase * sunTransmittance + multiscattering);
|
|
|
|
|
|
|
|
vec3 Sint = (S - S * sampleTransmittance) / extinction;
|
|
|
|
L += throughput * Sint;
|
|
|
|
throughput *= sampleTransmittance;
|
|
|
|
}
|
|
|
|
|
|
|
|
fragColor = L;
|
2021-07-26 10:10:58 +00:00
|
|
|
}
|