weBIGeo Clouds: Real-Time Volumetric Cloud Rendering
—My bachelor's thesis at TU Wien, finished in March 2026. The goal was to render volumetric clouds from actual meteorological forecast data inside weBIGeo, a research platform for interactive 3D terrain visualization built on top of AlpineMaps. The motivating idea was something like: a hiker wants to know whether the summit sits above or below the cloud layer. Standard weather apps can't answer that because they collapse the atmosphere into a flat map. Volumetric rendering in 3D terrain can.
The input data comes from ICON-D2, the regional forecast model of the German Weather Service, at 2.2 km horizontal resolution and 65 vertical layers. That resolution is too coarse for the hiking use case to actually work, but building a complete pipeline from raw forecast files to real-time browser rendering turned out to be a substantial project regardless. Everything runs in a standard web browser via WebGPU.
The Pipeline
There are two stages. An offline preprocessing step runs once per forecast timestamp and produces a TMS tile hierarchy of compressed volume textures. The browser renderer streams those tiles on demand and ray-marches through them.
The synthesis runs as a WebGPU compute shader invoked from Python. It reads cloud cover fraction, liquid and ice water content, turbulent kinetic energy, temperature, and pressure from ICON-D2, and writes the volumetric extinction coefficient into 256x256x64 voxel tiles at zoom level 10. Vertical interpolation uses PCHIP onto a uniform metric grid, because model levels are non-uniformly spaced and vary with local terrain height. Before any of this, two inconsistencies in the ICON-D2 fields need correcting. Cloud fraction can be zero where condensate is present, and condensate can be depleted where cloud fraction remains non-zero. Both cause cloud regions to render as fully transparent, and the corrections had a large visual impact.

At 2.2 km resolution the upsampled fields are too smooth. The shader adds sub-grid detail by first displacing sample coordinates using TKE-driven fractional Brownian motion noise, approximating sub-grid turbulent mixing, and then modulating extinction with a regime-adaptive density factor. The approach is inspired by the Nubis pipeline, using inverted Worley fBm to carve out the rounded shapes typical of real clouds. Under stratus conditions smooth fBm perturbs cloud edges. Under convective conditions a multi-scale product of gradient fBm and Worley noise generates the rounded cauliflower texture of cumulus clouds. The two blend continuously with a convective weight derived from TKE.

The zoom-10 tiles get pooled down to zoom 4 by 2x2 mean pooling on the horizontal axes only. Vertical resolution stays fixed at every zoom level because thin cloud layers remain perceptually significant even at small map scales. Everything is encoded as BC4 block-compressed KTX2 textures with zstd supercompression. The total compressed output per timestamp averages around 86 MiB, and the whole preprocessing run takes about 33 seconds.
The Renderer
All tiles are packed into a single 3D texture atlas at runtime because WebGPU provides no texture array type and caps the number of texture bindings per shader. The atlas totals about 1.14 GiB, which exceeds Firefox's current WebGPU texture limit, so it currently requires a Chromium-based browser.
The ray-marcher alternates between a coarse search phase that skips empty air in large steps, and a fine integration phase that accumulates radiance and transmittance only where cloud density is non-zero. MIP level is selected independently for the horizontal and vertical ray components, since the tile axes have different texel sizes and the anisotropy worsens at lower zoom levels.
Lighting uses a three-term phase function loosely following the Nubis pipeline. A forward Henyey-Greenstein lobe (g=0.7) produces the silver-lining effect when looking toward the sun, a weak backward lobe adds slight brightening on the other side, and an isotropic term keeps cloud faces away from the sun from going completely dark. Solar transmittance combines Beer-Lambert with a powder sugar term for backlit cloud edges and a multiple-scattering approximation for deep interiors. The phase function is evaluated once per pixel before the march since the view and sun directions are constant.
The cloud pass runs at half resolution. Per-pixel jitter combines Interleaved Gradient Noise with the golden-ratio R1 sequence so that successive frames accumulate non-redundant sub-pixel samples. A TAAU pass reprojects a full-resolution history buffer using the transmittance-weighted mean depth from the ray-marcher, clips the history to current-frame variance to suppress ghosting, and blends transmittance in optical depth space rather than linearly. Because the current frame carries a sub-pixel jitter, the accumulation fills in sub-pixel detail without a separate upsampling step.
Results
The cloud pass costs around 2.25 ms at peak GPU time, well within the 33 ms budget for 30 fps. Even with no clouds at all there is a fixed cost of about 0.29 ms from the coarse search loop and TAAU pass running unconditionally. Fully overcast scenes are actually faster than patchy ones, because rays hit the minimum transmittance threshold and terminate early rather than traversing long empty stretches between fragments.
Qualitative evaluation compared output against EUMETSAT satellite composites and alpine webcam imagery at matched timestamps. Against satellite, large-scale cloud patterns and the distribution of clear and cloudy regions agree well. Against webcam, three concrete limitations show up: the vertical resolution is too coarse to resolve sharp fog layer boundaries, small cloud elements like wispy low-altitude puffs are not reproduced as distinct structures, and the tile resolution is too coarse to encode the surface texture of individual cumulus cells.

If I Did It Again
The dominant limitation is source data resolution, which I cannot do much about. Higher-resolution public forecast data for Austria does not appear to be available. The procedural detail synthesis is driven entirely by TKE from the model, which is a rough proxy for turbulence intensity but carries no information about actual cloud structure at sub-grid scales. Feeding real observations like radar or satellite into the synthesis step is the obvious direction, though it would be a significantly more complex problem.
The shadow map assumes a vertical light direction, so shadow shapes do not change with sun position at all. Temporal blending between consecutive forecast timestamps would add smooth animation instead of static hourly snapshots. And the TAAU implementation does not fully resolve the tradeoff between ghosting suppression and convergence speed during fast camera motion. Those would be the obvious next things.
Stack
- Preprocessing
- Python, WebGPU compute via wgpu
- Shaders
- WGSL
- Browser API
- WebGPU
- Texture format
- KTX2, BC4, zstd
- Platform
- weBIGeo / AlpineMaps
- Input data
- ICON-D2 (Deutscher Wetterdienst)