Primary implementation surface
These routes are generated from canonical source. Their exact status remains separate from implementation availability.
Implement physically coherent sky, atmosphere, and haze in Three.js r185 native WebGPU/TSL using unit-consistent scattering LUTs, depth-aware aerial perspective, ellipsoid-aware geometry, explicit invalidation, and measured pipeline evidence.
These routes are generated from canonical source. Their exact status remains separate from implementation availability.
This skill contributes to the following cross-skill owner graphs.
The sky is single-scattered sunlight through an exponentially falling atmosphere. Rayleigh (molecules) and Mie (aerosols) contributions integrate along the view ray:
$$L(\lambda) = \int_0^{s_{atm}} T(0,s)\,\big(\beta_R(\lambda)\,p_R(\theta) + \beta_M\,p_M(\theta)\big)\,T_{sun}(s)\,ds$$with Rayleigh scattering's $\lambda^{-4}$ law giving the blue sky and red sunsets:
$$\beta_R(\lambda) \propto \lambda^{-4}, \qquad p_R(\theta) = \tfrac{3}{16\pi}(1+\cos^2\theta)$$The skill precomputes transmittance and scattering into LUTs by compute pass (a function of altitude and sun angle), then applies depth-aware aerial perspective to scene geometry: distant objects blend toward in-scattered airlight as $L' = L\,T(d) + L_{air}(d)$.
Every image identifies what it proves. Page screenshots demonstrate the published presentation only; generated inputs demonstrate asset channels only; canonical acceptance still requires render-target readback and a schema-v2 bundle.
1 published image
concept proxy preview
The complete SKILL.md as loaded by agents — verbatim, rendered.
Throughput is won by architecture before code. The taught path is native
WebGPU on Three.js r185, WebGPURenderer from three/webgpu, TSL from
three/tsl, node materials, one RenderPipeline, and compute-generated
Hillaire/Bruneton-family scattering products sampled by bounded nodes per
pixel. A WebGL backend is a hard blocker for this skill; there is no alternate
renderer branch here.
Read references/atmosphere-system-contract.md before implementation. It defines the LUT contracts, capability gate, authored workload trials, measurement obligations, color/output ownership, depth contract, diagnostics, and the techniques replaced by the WebGPU/TSL architecture.
For any material, cloud, water, vegetation, or weather consumer, first read the
router's
physics-domain and interaction contract.
Publish one versioned LightingTransportSnapshot from this atmosphere model.
The snapshot declares:
sampleInstant: PhysicsInstant, descriptor
validity whose temporal domain is a PhysicsTimeInterval, producer and
parameter revisions, spatial support, update cadence, and interpolation/error
policy;physicsFrameId and disc angular radius;SampledChannel's quantity kind, SI unit, filter/error, and,
where applicable, spectral/angular basis and conversion revision; a
provider-level transform registry never overrides channel metadata;PhysicsInstant;attenuationFactorIds for every output and explicit
skyIncludesDirectSolarDisc state for sky irradiance. A boolean
"attenuated" flag is insufficient.Consumers must choose either a pre-attenuated direct-light value or an unattenuated source plus the atmosphere transmittance provider. They may not use both. Cloud optical-depth shadows, opaque-geometry visibility, and water extinction are separate factors and each is applied exactly once. Do not bake cloud attenuation into this atmosphere snapshot or let a material reapply aerial perspective already owned by the image path.
EnvironmentForcingSnapshot is a separate thermodynamic/mechanical interface
sampled at sampleInstant: PhysicsInstant; use PhysicsTimeInterval only for
an actual validity or graph-stage interval. If temperature, humidity, pressure,
aerosol loading, or wind drive atmosphere parameters, record that forcing
revision and the transfer model; do not treat RGB extinction coefficients as a
wind or humidity provider.
After every physical owner commits, publish one view-independent
PhysicsPresentationCandidate with
requestedPresentationInstant: PhysicsInstant, containing the atmosphere's
base-provider presentedStatePairs, resourceLeases, and
eventSequenceRanges. Each
pair gives previousPresented.provenance and currentPresented.provenance
their own full PresentationSampleProvenance; never share one bracket, clock
map, or interpolation alpha across the two states. Each arm also carries its
own presentedInstant: PhysicsInstant. Static or low-rate products still
declare explicit hold or not-interpolated provenance.
For each target/view, the camera owner publishes CameraViewPublication with
previousRenderSampleInstant: PhysicsInstant,
currentRenderSampleInstant: PhysicsInstant, and
globalToRenderPrevious/globalToRenderCurrent plus view/projection matrices,
jitter, viewport, and depth state. Sky, aerial, shadow, cache, visibility, and
reset preparation then publishes a ViewPreparationPublication
against that camera record and the immutable candidate, owning
visibilityPublicationRefs, accelerationPublicationRefs,
shadowViewPublicationRefs, cachePublicationRefs, reactiveEpochs,
reactivePublications, resetDependencies, full resourceLeases for newly
created camera-dependent generations, and resourceLeaseRefs. Beyond
identity, target/view scope, seal metadata, and scoped eventSequenceRanges, the sealed
PhysicsPresentationSnapshot carries candidateId, cameraPublicationId,
viewPreparationId, presentedStatePairRefs, and resourceLeaseRefs; it never
copies PresentedStatePair records or globalToRender transforms. Leases
remain live through every consumer.
Any tier transition that changes physics-facing state or providers, update
cadence, represented support or spectral/spatial/temporal filter, error bounds,
inventories, stable IDs/RNG streams, or event and exact-once
application-ledger cursors requires the contract's shared QualityTransition.
Commit its conservative map atomically at a safe graph-step boundary and retain
old resources through the completion join. A strictly render-only LUT,
resolution, sampling, or composition change may remain local only when all
physics-provider semantics, PhysicsGraph ownership, committed physical
versions, IDs, cursors, and physical error bounds remain unchanged.
Canonical implementation contract: examples/webgpu-lut-atmosphere/.
Run node examples/webgpu-lut-atmosphere/validation.js after edits.
Legacy WebGL implementation (deprecated, do not extend): examples/lut-aerial-perspective/atmosphere-effect.js.
Lead with precomputed scattering, not per-pixel full scattering integration:
atmosphere parameters + planet/ellipsoid transform
-> compute transmittance LUT
-> compute multiscatter / irradiance LUTs
-> compute sky-view LUT for the active camera/sun frame
-> compute aerial inscattering plus RGB optical-depth froxels, or prove an
endpoint-transmittance reconstruction
-> scene pass() with shared color/depth ownership
-> TSL sky and aerial-perspective nodes sample LUTs
-> one HDR RenderPipeline output path
This architecture amortizes expensive optical-depth integration into compute dispatches and replaces nested view/light marches in every visible pixel with texture lookups, segment transmittance, and depth-aware composition.
$threejs-choose-skills preflight when atmosphere touches terrain,
clouds, shadows, exposure, or post ownership.beta [length^-1] * ds [length] must be
dimensionless. Never combine meter radii with per-kilometer coefficients.WebGPURenderer, call await renderer.init(), and require
renderer.backend.isWebGPUBackend === true before allocating resources.Fn().compute(count) dispatches through
renderer.compute() or renderer.computeAsync(). Write 2D products with
StorageTexture plus textureStore(); write volume products with r185
Storage3DTexture plus storageTexture3D(). Treat all products as
NoColorSpace data. Set r185 2D StorageTexture.generateMipmaps = false
and mipmapsAutoUpdate = false unless a measured path consumes authored
mips. Set Storage3DTexture.generateMipmaps = false as well when only its
base level is written; it has no mipmapsAutoUpdate property.
After initialization use renderer.compute() for normal submission. In
r185 computeAsync() only initializes on demand before enqueueing; it is
not a GPU-completion fence. Later GPU submissions are queue-ordered, but CPU
readback, timestamp resolution, resource reuse, and lifetime decisions need
an explicit completion/readback mechanism.RenderPipeline, pass(), and mrt() when the image chain needs
shared signals. In r185, PassNode.getViewZNode() is the standard/reversed
perspective helper and getLinearDepthNode() returns normalized depth,
not metric ray distance. Orthographic and logarithmic depth require their
explicit TSL conversions and fixed projection tests.Use the canonical validation module for Phase 1 structural/equation gates:
node examples/webgpu-lut-atmosphere/validation.js
It verifies the LUT manifest, RGBA16F upload policy, unit conversion fixtures,
manifest/live-model equality, CPU segment/intersection math, transmittance
mapping, HG normalization through the accepted |g| <= 0.99 interval, and
projection-specific depth reconstruction including nearest-covered-sample MSAA
resolve. It also builds the TSL graphs. It does not initialize a GPU, submit
compute, render a scene, or measure full-frame performance; those remain
browser-harness obligations.
Any path using compute/storage/MRT must gate after renderer initialization:
await renderer.init();
if ( renderer.backend.isWebGPUBackend !== true ) {
throw new Error(
'threejs-sky-atmosphere-and-haze requires a native WebGPU backend.'
);
}
// Full tier: compute-generated LUTs, storage textures, MRT/depth sharing.
Quality tiers:
Numeric labels used here and in the reference: [Derived] follows from an equation or representation; [Gated] is an acceptance ceiling/floor; [Measured] must come from a named device capture; [Authored] is a starting quality or appearance choice, never a hardware fact.
| Tier | Requirements | Authored starting dimensions |
|---|---|---|
| Full-detail trial | Native WebGPU; two RGB aerial payloads or proven endpoint reconstruction | [Authored] 256x64 transmittance, 64x32 multiscatter/irradiance, 192x108 sky-view, 192x108x32-48 aerial |
| Budgeted trial | Native WebGPU; cached static LUTs and staggered view products | [Authored] 192x48 transmittance, 128x64 sky-view, 160x90x24-32 aerial |
| Minimum-resident trial | Native WebGPU; one scene pass, compact view products, no hidden full refresh | [Authored] 128x32 transmittance, 96x48 sky-view, 96x54x16-24 aerial |
MeshStandardNodeMaterial or
MeshPhysicalNodeMaterial lighting integration;Do not inherit a device-class millisecond or memory table. Derive the workload:
payloadBytes = sum(width * height * depth * bytesPerTexel * residentCopies)
invocations = width * height * depth
workgroupInvocations = wgX * wgY * wgZ
r185FlattenedGroups = ceil(invocations / workgroupInvocations)
integratorSamples = updatedTexels * samplesPerTexel
This formula matches a numeric r185 Fn().compute(count, [wgX,wgY,wgZ]):
the backend dispatches [r185FlattenedGroups,1,1], wrapping into Y only when
the adapter's maxComputeWorkgroupsPerDimension is exceeded. Count unique
kernels, not output textures; one aerial kernel may write both RGB
inscattering and RGB optical depth.
The product supplies its own full-frame GPU p95, CPU-submit p95, presented-frame p95, and peak-live-byte budgets. Acceptance uses contemporaneous full-frame captures on the named adapter/browser/viewport/DPR/pass graph. Quality-table dimensions are [Authored] trials, not memory gates or timing evidence.
Do not spend full-resolution nested optical-depth marches per pixel. Every enabled tier must state texture dimensions, storage formats, dispatch counts, render resolution, update cadence, draw calls, peak live bytes, full-frame p50/p95, and resize/disposal behavior.
NoColorSpace linear data. Albedo/color art textures use
SRGBColorSpace.HalfFloatType working buffers until tone mapping.renderOutput() owns final presentation, set
RenderPipeline.outputColorTransform = false; otherwise let the host
RenderPipeline.outputColorTransform own conversion.renderer.reversedDepthBuffer, or getLinearDepthNode() is treated
as a metric distance;g approaches its singular |g|=1 limit without a declared accepted
interval, stable denominator, and normalization/extreme-direction tests;This skill owns molecular/aerosol sky scattering, sun/moon transmittance,
material sky irradiance, and depth-based surface-segment aerial perspective.
Use $threejs-image-pipeline for whole-frame HDR/depth/MRT ownership,
$threejs-exposure-color-grading for metering and tone mapping,
$threejs-volumetric-clouds for weather-shaped cloud density and cloud
shadows, $threejs-procedural-planets for planet terrain/material fields, and
$threejs-visual-validation for fixed-view diagnostics and GPU timing evidence.
This skill is the unique producer of the atmosphere-derived
LightingTransportSnapshot for a routed scene.
Preserved concept proxies and generated-asset previews. They are excluded from primary completion counts and link to the canonical lab through the schema-v2 registry.