WebGPU Volumetric Clouds: Atmospheric Raymarching & Henyey-Greenstein Phase Shaders
Rendering physically plausible atmospheric clouds in the browser at 60 FPS requires shifting beyond static textured 2D billboarding into real-time 3D volumetric raymarching. Using WebGPU compute and fragment shaders, developers sample procedural 3D Perlin-Worley noise volumes along primary camera rays, calculating optical depth and light extinction via the Beer-Lambert law and anisotropic forward-scattering with the Henyey-Greenstein phase function.
The Architecture of Atmospheric Raymarching
How primary camera rays and secondary sun light-marching rays integrate volumetric luminance:
At each step along the primary camera ray ($P$), the shader evaluates local cloud density ($ ho$). It then marches a secondary ray toward the directional light source to calculate optical depth ($ au$). The fraction of sunlight reaching the sample point is governed by exponential extinction ($T = e^{- au}$), modulated by the Henyey-Greenstein phase function ($P_{HG}( heta, g)$) to produce silver lining illumination.
Volumetric Cloud Rendering Techniques Compared
| Approach | Memory Footprint | Frame Latency (4K) | Dynamic Flight Penetration |
|---|---|---|---|
| 2D Skybox Cards | < 5 MB (Static PNGs) | 0.2 ms | Impossible (Flat geometry) |
| Particle Billboards | 20 - 50 MB | 4.5 ms (Severe overdraw) | Moderate (Clipping artifacts) |
| WebGPU Volumetric Raymarching | 32 MB (3D RGBA noise texture) | 2.1 ms (Temporal upsampling) | 100% Volumetric (Continuous traversal) |
WGSL Henyey-Greenstein Phase Shader Implementation
Real-time anisotropic forward/backward light scattering in WGSL:
// WGSL Henyey-Greenstein scattering phase function
fn henyeyGreenstein(cosTheta: f32, g: f32) -> f32 {
let PI = 3.14159265359;
let g2 = g * g;
let denom = 1.0 + g2 - 2.0 * g * cosTheta;
return (1.0 - g2) / (4.0 * PI * pow(max(denom, 0.0001), 1.5));
}
// Dual-lobe phase function for silver lining rim highlights
fn cloudPhase(cosTheta: f32) -> f32 {
let forwardScattering = henyeyGreenstein(cosTheta, 0.7); // Forward silver lining
let backwardScattering = henyeyGreenstein(cosTheta, -0.2); // Soft back-glow
return mix(backwardScattering, forwardScattering, 0.65);
}
// Volumetric Beer-Lambert transmittance integration
fn marchLightRay(samplePos: vec3, lightDir: vec3, densityTex: texture_3d, samp: sampler) -> f32 {
var opticalDepth: f32 = 0.0;
let stepSize: f32 = 25.0;
var currentPos = samplePos;
for (var i: i32 = 0; i < 6; i = i + 1) {
currentPos = currentPos + lightDir * stepSize;
let density = textureSampleLevel(densityTex, samp, currentPos * 0.0001, 0.0).r;
opticalDepth = opticalDepth + density * stepSize;
}
let absorptionCoeff: f32 = 0.05;
return exp(-opticalDepth * absorptionCoeff);
}
Explore Advanced WebGPU & Computer Graphics
Scale high-performance interactive web 3D graphics. Read our engineering guide on Procedural Mesh Generation & Dual Marching Cubes, explore Linux KPTI side-channel mitigations on WinWinHost KPTI Infrastructure, review V8 TurboFan Torque built-in internals on WebDesigner V8 CSA Compilers, or partner with our creative graphics engineering team.