Design & Brand Philosophy

WebGPU Volumetric Clouds: Atmospheric Raymarching & Henyey-Greenstein Phase Shaders

By Creative Direction Team

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:

☁️ The Beer-Lambert Transmittance Invariant

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 msImpossible (Flat geometry)
Particle Billboards20 - 50 MB4.5 ms (Severe overdraw)Moderate (Clipping artifacts)
WebGPU Volumetric Raymarching32 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.