Skip to content

Sky & Atmosphere Shaders

Physically-based atmospheric scattering shaders for procedural sky rendering with Rayleigh and Mie scattering.

import {
skyVertexShader,
skyFragmentShader,
atmosphereVertexShader,
atmosphereFragmentShader,
} from '@strata-game-library/shaders';

Complete procedural sky with sun and atmospheric effects:

UniformTypeDescription
sunPositionvec3Sun position in world space
turbidityfloatAtmospheric haziness (2-20)
rayleighfloatBlue scattering coefficient
mieCoefficientfloatSun halo coefficient
mieDirectionalGfloatHalo directionality (0-1)
sunIntensityfloatSun brightness
import { skyVertexShader, skyFragmentShader } from '@strata-game-library/shaders';
const skyMaterial = new THREE.ShaderMaterial({
vertexShader: skyVertexShader,
fragmentShader: skyFragmentShader,
uniforms: {
sunPosition: { value: new THREE.Vector3(100, 50, 100) },
turbidity: { value: 10 },
rayleigh: { value: 2 },
mieCoefficient: { value: 0.005 },
mieDirectionalG: { value: 0.8 },
sunIntensity: { value: 1.0 },
},
side: THREE.BackSide,
});
const sky = new THREE.Mesh(
new THREE.SphereGeometry(5000, 32, 32),
skyMaterial
);

Standalone atmospheric scattering for planet rendering:

UniformTypeDescription
uSunDirectionvec3Normalized sun direction
uAtmosphereRadiusfloatOuter atmosphere radius
uPlanetRadiusfloatPlanet surface radius
uRayleighScatteringvec3RGB scattering coefficients
uMieScatteringfloatMie scattering coefficient
uRayleighScaleHeightfloatRayleigh density falloff
uMieScaleHeightfloatMie density falloff
import { atmosphereVertexShader, atmosphereFragmentShader } from '@strata-game-library/shaders';
const atmosphereMaterial = new THREE.ShaderMaterial({
vertexShader: atmosphereVertexShader,
fragmentShader: atmosphereFragmentShader,
uniforms: {
uSunDirection: { value: new THREE.Vector3(0, 1, 0).normalize() },
uAtmosphereRadius: { value: 6420000 },
uPlanetRadius: { value: 6360000 },
uRayleighScattering: { value: new THREE.Vector3(5.5e-6, 13.0e-6, 22.4e-6) },
uMieScattering: { value: 21e-6 },
uRayleighScaleHeight: { value: 8000 },
uMieScaleHeight: { value: 1200 },
},
side: THREE.BackSide,
transparent: true,
});
vec3 rayleighScattering(float cosTheta, vec3 lambda) {
// Rayleigh scattering coefficient
vec3 beta = (8.0 * pow(PI, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0)) /
(3.0 * N * pow(lambda, vec3(4.0)));
// Phase function
float phase = (3.0 / 16.0 * PI) * (1.0 + cosTheta * cosTheta);
return beta * phase;
}
float miePhase(float cosTheta, float g) {
float g2 = g * g;
return (3.0 / (8.0 * PI)) *
((1.0 - g2) * (1.0 + cosTheta * cosTheta)) /
(pow(1.0 + g2 - 2.0 * g * cosTheta, 1.5) * (2.0 + g2));
}
float atmosphericDensity(float altitude, float scaleHeight) {
return exp(-altitude / scaleHeight);
}
vec2 raySphereIntersect(vec3 rayOrigin, vec3 rayDir, float sphereRadius) {
float a = dot(rayDir, rayDir);
float b = 2.0 * dot(rayOrigin, rayDir);
float c = dot(rayOrigin, rayOrigin) - sphereRadius * sphereRadius;
float d = b * b - 4.0 * a * c;
if (d < 0.0) return vec2(-1.0);
return vec2(
(-b - sqrt(d)) / (2.0 * a),
(-b + sqrt(d)) / (2.0 * a)
);
}
// Dawn
uniforms.sunPosition.value.set(100, 5, 0);
uniforms.turbidity.value = 4;
uniforms.rayleigh.value = 2;
// Noon
uniforms.sunPosition.value.set(0, 100, 0);
uniforms.turbidity.value = 10;
uniforms.rayleigh.value = 1;
// Sunset
uniforms.sunPosition.value.set(-100, 5, 0);
uniforms.turbidity.value = 5;
uniforms.rayleigh.value = 3;
// Night
uniforms.sunPosition.value.set(0, -100, 0);
uniforms.turbidity.value = 2;
uniforms.rayleigh.value = 0.1;