@yong_three/three-clouds
v0.1.3
Published
A Three.js and R3F implementation of geospatial volumetric clouds
Maintainers
Readme
@yong_three/three-clouds — WebGPU
This branch contains the WebGPU implementation of geospatial volumetric
clouds. It is built on the Three.js node API and is published under the
@yong_three/three-clouds package scope. It is not an official Takram release.
Production demo
Open the live WebGPU Storybook at
clouds.ceo-online.app.
The demo contains three examples:

Installation
The published 0.1.3 release targets Three.js 0.184.x:
npm install @yong_three/[email protected] [email protected] postprocessing
npm install --save-dev @types/[email protected]The WebGPU entry point is:
import { clouds } from '@yong_three/three-clouds/webgpu'The complete reference scenes also use the fork's atmosphere and core changes. For exact scene-surface shadow parity, use this monorepo until the companion fork packages are published.
Quick start
CloudsNode is composited into a Three.js RenderPipeline together with an
atmosphere context and a scene color/depth pass. The renderer must request at
least 32 sampled textures per shader stage.
import { WebGPURenderer } from 'three/webgpu'
import { clouds } from '@yong_three/three-clouds/webgpu'
const renderer = new WebGPURenderer({
requiredLimits: {
maxSampledTexturesPerShaderStage: 32
}
})
await renderer.init()
const cloudsNode = await clouds(depthNode).loadDefaultTexturesAsync()
cloudsNode.coverage = 0.3
cloudsNode.qualityPreset = 'high'
cloudsNode.localWeatherVelocity.set(0.001, 0)Use the complete Clouds-Vanilla.tsx
example for a standalone Three.js setup, or
Clouds-Basic.tsx
for the Storybook integration.
Supported features
- Volumetric cloud raymarching with weather, shape, detail, turbulence, haze, phase function, aerial perspective, powder, and ground bounce
- Cascaded beer shadow maps (BSM) with temporal resolve
- Temporal upscaling and full-resolution temporal resolve
- Light shafts through resolved cloud shadow length
- Cloud animation through weather, shape, and detail velocity uniforms
- Custom cloud layers through
CloudLayersandsetCloudLayers() - Cloud shadows on scene geometry through
getSunTransmittanceNode()andAerialPerspectiveNode.sunTransmittanceNode - Quality presets, default texture loading, and runtime debug views
Custom layers
import { CloudLayers } from '@yong_three/three-clouds'
import { clouds } from '@yong_three/three-clouds/webgpu'
const layers = new CloudLayers([
{
channel: 'r',
altitude: 1000,
height: 1000,
shapeAmount: 0.8,
weatherExponent: 0.6,
shadow: true
},
{
channel: 'g',
altitude: 2000,
height: 800,
densityScale: 0.1
}
])
const cloudsNode = clouds(depthNode).loadDefaultTextures()
cloudsNode.setCloudLayers(layers)See Clouds-CustomLayers.tsx
for the complete four-layer scene.
Scene-surface shadows
Cloud self-shadowing and scene-surface shadows are separate paths. Connect the cloud optical transmittance node to the atmosphere aerial-perspective node:
aerialNode.sunTransmittanceNode = (positionECEF, builder) =>
cloudsNode.getSunTransmittanceNode(positionECEF, builder)
postProcessing.needsUpdate = trueThe scene pass must include normals. Changing this callback requires rebuilding the render pipeline.
Loading and animation
loadDefaultTexturesAsync() waits for all hosted assets and rejects on a
loading error. loadDefaultTextures() starts loading immediately and returns
the node synchronously.
cloudsNode.localWeatherVelocity.set(0.001, 0)
cloudsNode.shapeVelocity.set(0.0001, 0.0001, 0.0001)
cloudsNode.shapeDetailVelocity.set(0.0002, 0.0002, 0.0002)Integration options
The WebGPU factory accepts a second argument so applications do not need to patch the package source or Vite configuration:
import { clouds } from '@yong_three/three-clouds/webgpu'
const cloudLayer = clouds(depthNode, {
ellipsoid: gameEllipsoid,
curvature: {
referenceRadius: 6_360_000,
planetRadius: 63_710,
preserveLocalScale: true
},
depth: { mode: 'reversed-z', epsilon: 1e-7 },
quality: {
preset: 'high',
bsm: true,
lightShafts: true,
haze: true,
temporalUpscale: true
},
shadows: { dispatchMode: 'automatic' }
}).loadDefaultTextures({ assetBaseUrl: new URL('./assets/', import.meta.url) })depth.mode selects the scene-depth comparison (conventional or
reversed-z). ellipsoid is used for camera geodetic height instead of
implicitly using WGS84. curvature is carried as node configuration for
planet-scale integrations and keeps the reference and game radii explicit.
When both referenceFrame and planetFrame are supplied, their east/north/up
bases are converted to a GPU matrix and applied to cloud and shadow shape
sampling.
The cloud/atmosphere WGSL helper is emitted as getCloudLayerDensity, so the
two pipelines can be composed without a Vite string replacement.
Runtime controls are available on the returned CloudsNode:
cloudLayer.setEnabled(false)
cloudLayer.setCoverage(0.35)
cloudLayer.setQuality({ preset: 'medium', bsm: false })
cloudLayer.maxRayDistance = 100_000
cloudLayer.resetHistory()
cloudLayer.updateShadowMaps(frame) // only when dispatchMode is 'explicit'
cloudLayer.dispose()setEnabled() updates the cloudsEnabled GPU uniform. It is safe to call
after the renderer has built the pipeline; no material or shader rebuild is
triggered.
Set shadows.enabled: false to skip the BSM dispatch entirely. For an
application-owned frame graph, use dispatchMode: 'explicit' and call
updateShadowMaps(frame) exactly once per frame.
Current limitations
- Advanced parameters remain available through
parameterUniforms,marchNode,shadowNode, andresolveNode. - 3D Tiles and world-origin-rebasing scenes are not ported in this branch.
- Procedural texture nodes are supported, but the validated parity path uses the hosted default texture assets.
Local Storybook
pnpm install
pnpm nx storybook storybook-webgpu --port=4004 --no-openThen open:
Recommended checks:
pnpm exec tsc --noEmit -p storybook-webgpu/tsconfig.storybook.json
pnpm exec nx typecheck clouds
pnpm exec nx test clouds
pnpm exec nx build clouds