threejs-volumetric-clouds skill (Threejs-Awesome-Graphics-Agent-Skills)
- Install
- SKILL.md (verbatim)
- System order
- Required controls
- Failure conditions
- Routing boundary
- Other files in this skill
- assets/weather-volume-clouds/THIRDPARTYLICENSES.md (verbatim)
- references/weather-volume-and-reconstruction.md (verbatim)
- Contents
- 1. Preserve the four-layer vector model
- 2. Understand the packed interval subtlety
- 3. Generate field assets once
- Local weather RGBA
- Base shape volume
- Detail volume
- Turbulence
- 4. Weather coverage response
- 5. Base shape and turbulence
- 6. Detail changes topology by height
- 7. Planetary ray interval
- 8. Primary march policy
- 9. Lighting contract
- 10. Quarter-resolution temporal upscale
- 11. Cloud shadow representation
- 12. Quality tiers must remove expensive mechanisms intentionally
- 13. Required diagnostics
- 14. Failure diagnosis
What it does. Implement volumetric cloud systems in Three.js. Use for weather-driven density, bounded raymarching, shape/detail erosion, vertical profiles, lighting cones, silver lining, temporal reconstruction, cloud shadows, multiple layers, and scalable quality modes. Part of scottstts/Threejs-Awesome-Graphics-Agent-Skills (scottstts/Threejs-Awesome-Graphics-Agent-Skills).
| Upstream | scottstts/Threejs-Awesome-Graphics-Agent-Skills |
| Skill file | skills/threejs-volumetric-clouds/SKILL.md |
| License | MIT |
| Author | Scott Sun (scottstts) |
| Fetched | 2026-09-10 |
Install
npx skills add scottstts/Threejs-Awesome-Graphics-Agent-Skills --skill threejs-volumetric-clouds, or copy the skill folder into~/.claude/skills/threejs-volumetric-clouds/.- Raw file:
curl -sL https://raw.githubusercontent.com/scottstts/Threejs-Awesome-Graphics-Agent-Skills/HEAD/skills/threejs-volumetric-clouds/SKILL.md
SKILL.md (verbatim)
name: threejs-volumetric-clouds
description: Implement volumetric cloud systems in Three.js. Use for weather-driven density, bounded raymarching, shape/detail erosion, vertical profiles, lighting cones, silver lining, temporal reconstruction, cloud shadows, multiple layers, and scalable quality modes.
Volumetric Clouds
Cloud quality comes from density organization, lighting, and temporal stability—not from increasing march steps over unstructured noise.
This skill contains exemplary examples and assets beyond descriptive guidance, they're worth studying, referencing, or even copying. Use them sufficiently when relevant and do NOT blindly skip them.
System order
- Define the cloud volume and layer bounds.
- Generate or source weather, base-shape, detail, and turbulence fields.
- Build a density function with vertical and weather profiles.
- Raymarch only the bounded occupied segment.
- Integrate transmittance and lighting front-to-back.
- Reconstruct low-resolution output temporally.
- Project a separate low-cost cloud-shadow solution.
Read references/weather-volume-and-reconstruction.md before implementing or auditing the cloud system.
Read the weather volume cloud entry
and its source/ modules for weather-layer ownership, spherical shell bounds,
authored shape/detail sampling, cloud shadow maps, temporal upscale,
atmospheric composition, and package-owned diagnostics.
Required controls
- coverage, cloud type, precipitation, and anvil bias;
- base/top altitude and vertical density profile;
- shape/detail scales and erosion;
- wind for each field;
- primary step count, light step count, and empty-space policy;
- history weight and disocclusion threshold;
- cloud-shadow extent, resolution, and update rate.
Failure conditions
- density is only
fbm(position); - the raymarch traverses the full camera range;
- detail noise adds density instead of eroding shaped masses;
- temporal history is accepted across disocclusion;
- shadows use the full beauty raymarch;
- every cloud layer shares the same wind and density profile.
Routing boundary
Use $threejs-atmosphere-aerial-perspective for molecular/aerosol scattering
without weather density. Use $threejs-procedural-vfx for emissive aurora
curtain slabs or bounded interactive voxel fire and smoke with velocity,
pressure, emitter, and collision fields. This skill owns weather-shaped cloud
volumes, reconstruction, cloud lighting, and cloud shadows.
Other files in this skill
- agents/openai.yaml
- assets/weather-volume-clouds/THIRD_PARTY_LICENSES.md
- assets/weather-volume-clouds/atmosphere/irradiance.exr
- assets/weather-volume-clouds/atmosphere/scattering.exr
- assets/weather-volume-clouds/atmosphere/transmittance.exr
- assets/weather-volume-clouds/local_weather.png
- assets/weather-volume-clouds/shape.bin
- assets/weather-volume-clouds/shape_detail.bin
- assets/weather-volume-clouds/stbn.bin
- assets/weather-volume-clouds/turbulence.png
- examples/weather-volume-clouds/cloud-effect.js
- examples/weather-volume-clouds/source/atmosphere/AerialPerspectiveEffect.ts
- examples/weather-volume-clouds/source/atmosphere/AtmosphereMaterialBase.ts
- examples/weather-volume-clouds/source/atmosphere/AtmosphereParameters.ts
- examples/weather-volume-clouds/source/atmosphere/IrradianceMaskPass.ts
- examples/weather-volume-clouds/source/atmosphere/PrecomputedTexturesLoader.ts
- examples/weather-volume-clouds/source/atmosphere/SkyLightProbe.ts
- examples/weather-volume-clouds/source/atmosphere/SkyMaterial.ts
- examples/weather-volume-clouds/source/atmosphere/StarsGeometry.ts
- examples/weather-volume-clouds/source/atmosphere/StarsMaterial.ts
- examples/weather-volume-clouds/source/atmosphere/SunDirectionalLight.ts
- examples/weather-volume-clouds/source/atmosphere/blackBodyChromaticity.ts
- examples/weather-volume-clouds/source/atmosphere/celestialDirections.ts
- examples/weather-volume-clouds/source/atmosphere/constants.ts
- examples/weather-volume-clouds/source/atmosphere/getAltitudeCorrectionOffset.ts
- examples/weather-volume-clouds/source/atmosphere/getSunLightColor.ts
- examples/weather-volume-clouds/source/atmosphere/helpers/functions.ts
- examples/weather-volume-clouds/source/atmosphere/helpers/sampleTexture.ts
- examples/weather-volume-clouds/source/atmosphere/index.ts
- examples/weather-volume-clouds/source/atmosphere/shaders/aerialPerspectiveEffect.frag
- examples/weather-volume-clouds/source/atmosphere/shaders/aerialPerspectiveEffect.vert
- examples/weather-volume-clouds/source/atmosphere/shaders/functions.glsl
- examples/weather-volume-clouds/source/atmosphere/shaders/index.ts
- examples/weather-volume-clouds/source/atmosphere/shaders/irradianceMask.frag
- examples/weather-volume-clouds/source/atmosphere/shaders/parameters.glsl
- examples/weather-volume-clouds/source/atmosphere/shaders/sky.frag
- examples/weather-volume-clouds/source/atmosphere/shaders/sky.glsl
- examples/weather-volume-clouds/source/atmosphere/shaders/sky.vert
- examples/weather-volume-clouds/source/atmosphere/shaders/stars.frag
- examples/weather-volume-clouds/source/atmosphere/shaders/stars.vert
- examples/weather-volume-clouds/source/atmosphere/types.ts
- examples/weather-volume-clouds/source/clouds/CascadedShadowMaps.ts
- examples/weather-volume-clouds/source/clouds/CloudLayer.ts
- examples/weather-volume-clouds/source/clouds/CloudLayers.ts
- examples/weather-volume-clouds/source/clouds/CloudShape.ts
- examples/weather-volume-clouds/source/clouds/CloudShapeDetail.ts
- examples/weather-volume-clouds/source/clouds/CloudsEffect.ts
- examples/weather-volume-clouds/source/clouds/CloudsMaterial.ts
- examples/weather-volume-clouds/source/clouds/CloudsPass.ts
- examples/weather-volume-clouds/source/clouds/CloudsResolveMaterial.ts
- examples/weather-volume-clouds/source/clouds/DensityProfile.ts
- examples/weather-volume-clouds/source/clouds/LocalWeather.ts
- examples/weather-volume-clouds/source/clouds/PassBase.ts
- examples/weather-volume-clouds/source/clouds/Procedural3DTexture.ts
- examples/weather-volume-clouds/source/clouds/ProceduralTexture.ts
- examples/weather-volume-clouds/source/clouds/ShaderArrayPass.ts
- examples/weather-volume-clouds/source/clouds/ShadowMaterial.ts
- examples/weather-volume-clouds/source/clouds/ShadowPass.ts
- examples/weather-volume-clouds/source/clouds/ShadowResolveMaterial.ts
- examples/weather-volume-clouds/source/clouds/Turbulence.ts
- ... and 89 more (see the folder)
assets/weather-volume-clouds/THIRD_PARTY_LICENSES.md (verbatim)
Third-party licenses
The atmosphere lookup tables and cloud textures in this directory are third-party assets used under the MIT License reproduced below.
MIT License
Copyright (c) 2025 Su
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
references/weather-volume-and-reconstruction.md (verbatim)
Weather-shaped cloud volume and reconstruction
Use this reference for planetary or large-world volumetric clouds built from packed weather layers, generated shape and detail fields, bounded integration, temporal reconstruction, and low-cost cloud shadows.
Contents
- Preserve the four-layer vector model
- Understand the packed interval subtlety
- Generate field assets once
- Weather coverage response
- Base shape and turbulence
- Detail changes topology by height
- Planetary ray interval
- Primary march policy
- Lighting contract
- Quarter-resolution temporal upscale
- Cloud shadow representation
- Quality tiers must remove expensive mechanisms intentionally
- Required diagnostics
- Failure diagnosis
1. Preserve the four-layer vector model
Evaluate four layers in parallel as vec4 channels. Do not collapse them into
one scalar weather field before applying per-layer altitude, profile, shape,
and detail controls.
Default active layers:
| Channel | Altitude | Height | Density | Shape | Detail | Coverage width | Shadow |
|---|---|---|---|---|---|---|---|
| R low | 750 m | 650 m | 0.2 | 1.0 | 1.0 | 0.6 | yes |
| G middle | 1000 m | 1200 m | 0.2 | 1.0 | 1.0 | 0.6 | yes |
| B high | 7500 m | 500 m | 0.003 | 0.4 | 0.0 | 0.5 | no |
| A spare | disabled | disabled | default | default | default | default | no |
Each layer also owns:
type Layer = {
weatherChannel: "r" | "g" | "b" | "a"
weatherExponent: number
shapeAlteringBias: number
densityProfile: {
exponentialTerm: number
exponent: number
linearTerm: number
constantTerm: number
}
}
Default density profile:
profile(h) =
expTerm * exp(exponent * h)
+ linearTerm * h
+ constantTerm
default = 0.75 * h + 0.25
The profile is not a generic bottom/top smoothstep. It is a compact artist-authored function that can produce rising, falling, or exponential density by layer.
2. Understand the packed interval subtlety
The layer system sorts all lower/upper altitude endpoints and merges occupied ranges. It then packs up to three intervals where the active-layer balance is zero.
Those packed intervals are empty gaps, despite names such as
minIntervalHeights and insideLayerIntervals.
Beauty and shadow marches use:
if (height is inside any packed gap) {
skip density work;
}
For the default layers, the low and middle ranges merge into one occupied band from 750–2200 m, followed by an empty gap before the 7500–8000 m high layer.
If adapting the system:
- merge occupied layer ranges on CPU;
- pack the complementary gaps;
- verify the debug view labels them as skipped intervals;
- do not accidentally skip the occupied bands.
3. Generate field assets once
Render procedural textures into repeatable targets, then sample them during the expensive march.
Local weather RGBA
The procedural weather texture assigns:
R: low-cloud Worley FBM
G: middle-cloud Worley FBM
B: high-cloud anisotropic Perlin
A: auxiliary Perlin variation
Low and middle fields are intentionally separated:
middle = smoothstep(1.0, 1.4, WorleyFBM(point + 0.5))
low = saturate(
smoothstep(0.8, 1.4, WorleyFBM(point))
- middle
)
The generation pass later forces alpha to 1, so the default spare A layer is
not a second generated weather population. Preserve that behavior when using
the complete packed-layer contract.
Base shape volume
The 3D base texture combines Perlin-Worley and Worley FBM:
perlinWorley =
remap(perlin, 0, 1, worleyFBM, 1)
baseShape =
remap(perlinWorley, worleyFBM - 1, 1)
The octave weights are dominated by low frequencies:
0.625, 0.25, 0.125
Detail volume
The detail volume is Worley-only and builds three progressively finer FBM
bands from frequencies 2, 4, 8, 16, again weighted toward low frequencies.
Turbulence
The 2D turbulence texture stores a normalized curl field derived from three offset Perlin channels. It warps shape coordinates; it is not multiplied into final density as arbitrary noise.
Generate these textures once or only when their recipes change. A 3D render-target path requires one draw per Z layer, so regenerating each frame is unacceptable.
4. Weather coverage response
For each sample:
heightFraction =
remapClamped(height, layerMin, layerMax)
Round cloud growth toward the upper portion:
biased = heightFraction ^ shapeAlteringBias
x = clamp(2 * biased - 1, -1, 1)
heightScale = 1 - x²
Sample the four selected weather channels, apply each layer’s exponent, then modulate by global coverage:
factor = 1 - coverage * heightScale
density =
remapClamped(
mix(localWeather, 1, coverageFilterWidth),
factor,
factor + coverageFilterWidth
)
Global coverage therefore shifts/remaps local weather. It is not a final density multiplier.
Debug separately:
raw local-weather channels
weather after exponent
heightScale
coverage factor
coverage-remapped density
5. Base shape and turbulence
Advect fields through offsets rather than regenerating them:
localWeatherOffset += localWeatherVelocity * dt
shapeOffset += shapeVelocity * dt
detailOffset += detailVelocity * dt
turbulenceOffset += turbulenceVelocity * dt
The density path adds a radial “evolution” offset related to weather speed, then optional turbulence:
surfaceNormal = normalize(position)
evolution = -surfaceNormal * length(weatherOffset) * 20000
turbulence =
displacement
* (curlTexture * 2 - 1)
* lowHeightMask
The turbulence mask fades out by roughly the lower 30% of each layer, so it distorts bases and growth without scrambling the entire cloud.
Base shape:
shapePosition =
(position + evolution + turbulence)
* shapeRepeat
+ shapeOffset
density =
remapClamped(
weatherDensity,
(1 - shapeNoise) * shapeAmount,
1
)
Shape amount is per layer. High cirrus uses less base-shape influence.
6. Detail changes topology by height
The detail modifier is not uniform erosion.
top modifier = detail^6
bottom modifier = 1 - detail
modifier =
mix(
top modifier,
bottom modifier,
remapClamped(heightFraction, 0.2, 0.4)
)
This makes upper cloud detail fluffy and lower detail whippy/eroded. Then:
modifier *= shapeDetailAmount
density =
remapClamped(
density * 2,
modifier * 0.5,
1
)
Detail is skipped at coarse mip levels using a jittered threshold. Do not spend high-frequency texture reads when the sample footprint cannot resolve them.
Finally:
densityVector =
saturate(
densityVector
* densityScales
* profile(heightFraction)
)
totalDensity = sum(densityVector)
layerWeight = densityVector / totalDensity
scattering = totalDensity * scatteringCoefficient
extinction =
totalDensity * absorptionCoefficient
+ scattering
Guard zero total density before using layer weights in an independent implementation.
7. Planetary ray interval
Intersect view rays with concentric spheres at:
planet radius
minimum cloud altitude
maximum cloud altitude
shadow top altitude
Select near/far based on camera state:
below clouds
inside total cloud layer
above clouds
ray intersects ground
Then clamp the far distance against opaque scene depth. The beauty march never runs beyond the nearest opaque surface.
Return explicit diagnostic flags:
ground intersection
scene occlusion
camera region
near distance
far distance
selected sphere intersections
8. Primary march policy
High/default values:
max iterations: 500
minimum step: 50 m
maximum step: 1000 m
maximum ray distance: 200 km
perspective step scale: 1.01
minimum density: 1e-5
minimum extinction: 1e-5
minimum transmittance: 1e-2
Initial step size grows with ray entry distance:
step =
minStep
+ (perspectiveScale - 1) * rayNear
Jitter the first step with spatiotemporal blue noise. Double the jitter distance when needed to suppress spatial aliasing.
At each sample:
- skip packed empty altitude gaps;
- sample rough weather;
- if all layer densities are below threshold, take a longer mip-aware step;
- otherwise sample base shape, optional turbulence, and detail;
- if extinction is significant, evaluate lighting and integrate;
- terminate at the transmittance threshold.
Long empty-space steps can band near a dense crossing. Treat this as a known defect, not a solved feature. A robust adaptation can binary-search the first dense crossing.
9. Lighting contract
Per occupied sample, evaluate:
sun irradiance
sky irradiance
short optical-depth march toward sun
Beer shadow-map optical depth beyond that short march
multi-scattering approximation
optional ground bounce
sky gradient contribution
powder attenuation
The phase function defaults to two Henyey-Greenstein lobes. An optional fitted large-particle phase path exists, but it requires adequate multiple scattering to remain plausible.
Multi-scattering uses octave accumulation:
for each octave:
contribution +=
attenuationA
* exp(-opticalDepth * attenuationB)
* phase(cosTheta, attenuationC)
attenuation *= 0.5
Default high quality uses eight octaves.
Energy-conserving integration:
stepT = exp(-extinction * stepLength)
stepScatter =
(radiance - radiance * stepT)
/ max(extinction, epsilon)
accumulatedRadiance += accumulatedT * stepScatter
accumulatedT *= stepT
Representative depth is a transmittance-weighted sample distance. It is used for aerial perspective and temporal velocity, not merely visualized.
10. Quarter-resolution temporal upscale
The temporal-upscale path renders the current clouds at one quarter linear resolution:
lowWidth = ceil(fullWidth / 4)
lowHeight = ceil(fullHeight / 4)
A 4×4 Bayer pattern chooses one current full-resolution pixel per low-resolution texel over 16 frames. Projection jitter follows the same offset.
Current targets store:
RGBA cloud radiance/transmittance
RGB representative depth + velocity
optional shadow length
Resolve:
- use the newly rendered current texel when its Bayer index matches the frame;
- otherwise choose the closest-depth sample in a 3×3 neighborhood;
- reproject with velocity;
- reject history outside the viewport;
- variance-clip history against current neighbors;
- write the resolved result and swap history buffers.
For full-resolution TAA, blend clipped history toward current with default
temporalAlpha = 0.1.
Reset history on:
camera cut
resolution or render-scale change
weather/shape discontinuity
layer topology change
projection mode change
11. Cloud shadow representation
The shadow system is not a grayscale beauty march. Each cascade stores:
R front depth
G mean extinction
B maximum accumulated optical depth
A optical-depth tail estimate after early termination
Beauty lighting reconstructs optical depth beyond a local short sun march from this compact representation.
Shadow marching uses structured volume sampling:
- choose one of three icosahedral structure normals from ray direction and jitter;
- intersect regularly spaced planes perpendicular to that normal;
- march samples on those planes.
This intentionally trades some spatial aliasing for strong temporal stability, which matters for low-resolution cascaded shadow maps.
Default shadow budget:
3 cascades
512 × 512 maps
50 iterations
100–1000 m step size
minimum transmittance 1e-4
Low tier:
2 cascades
256 × 256 maps
25 iterations
minimum transmittance 1e-2
12. Quality tiers must remove expensive mechanisms intentionally
The low tier disables:
light shafts
shape detail
turbulence
ground bounce
accurate sun/sky lookup
It keeps:
weather-shaped density
base 3D shape
one short sun march
temporal reconstruction
cloud shadows at reduced quality
This is a legitimate fallback because silhouette, weather control, and directional self-shadowing survive.
13. Required diagnostics
Expose:
weather RGBA
per-layer height fractions
packed empty intervals
coverage-remapped density
base shape
detail modifier
turbulence displacement
final per-layer density vector
total scattering/extinction
ray near/far and scene clamp
primary/shape/detail sample counts
sun optical depth
Beer shadow-map channels
transmittance
representative depth
velocity
history UV
variance bounds
history rejection
shadow cascade index
shadow structured-sampling planes
14. Failure diagnosis
clouds disappear between low and high layers:
occupied ranges were mistaken for packed empty gaps
all cloud types share one silhouette:
layer vectors were summed before profile/shape controls
porous smoke:
detail was added uniformly instead of height-dependent remapping
boiling motion:
field offsets use unrelated directions/speeds or textures regenerate
bright flat interior:
short sun optical depth or shadow map is missing
dark featureless cloud:
multi-scattering, sky light, or powder balance is absent
edge trails:
representative depth/velocity is wrong or history lacks variance clipping
flickering cloud shadows:
beauty jitter was reused instead of temporally stable structured sampling
cost scales with view distance:
shell interval, scene depth clamp, or empty-gap skipping is broken
Back to scottstts/Threejs-Awesome-Graphics-Agent-Skills or Agent skills.