Sky, Atmosphere, and Haze

Build sky, atmosphere, and haze in Three.js WebGPU/TSL. Use for authored sky/fog, planetary scattering, depth-aware aerial perspective, or atmosphere-derived sun/sky lighting.

Image: Atmosphere final composite readback. Source lab: webgpu-lut-atmosphere.

$threejs-sky-atmosphere-and-haze 1 primary target 1 flagship 1 secondary surface native evidence pending Latest skill update commit 8749183 ↗ SKILL.md on GitHub ↗ raw (for agents) ↗

Primary target surface

These rendering routes are generated from canonical source. Native-WebGPU acceptance remains separate from entrypoint availability.

Evidence reports

Source hashes, claim verdicts, promoted same-lab media, fixed routes, exact tier contracts, and current limitations.

The approach, mathematically

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)$.

Preview and evidence ledger

Every image identifies what it proves. Page screenshots demonstrate the published presentation only; generated inputs demonstrate asset channels only; rendering acceptance still requires same-lab readback and a schema-v2 bundle.

Canonical runtime evidence pending6 published images
Native WebGPU runtime evidence preview

Native WebGPU LUT Atmosphere

Evidence pending
visualCorrectness
PASS
mechanismCorrectness
PASS
performanceCompliance
INSUFFICIENT_EVIDENCE
gpuAttribution
INSUFFICIENT_EVIDENCE
lifecycleStability
INSUFFICIENT_EVIDENCE
  • Five-stage LUT chain, live depth/body composition, and cumulative aerial XY topology proved from the correctness bundle.
  • Named-adapter GPU timestamps, reference radiance/energy, and lifecycle stability remain residual.

The full skill

The complete SKILL.md as loaded by agents, rendered verbatim.

Sky, Atmosphere, and Haze

Build every branch around one atmosphere model, one scene-linear HDR path, and one final-output owner.

Process

1. Select the transport claim

Choose the least expensive branch that supports the requested observable:

Need Branch Valid claim
Regional authored sky, distance haze, or height haze SkyMesh plus TSL fog/haze Authored appearance in a bounded local frame
Real-time planetary sky and aerial perspective Compact Hillaire-style LUTs Approximate higher-order transport within measured error
Fixed atmosphere with stricter spectral/angular accuracy Bruneton-style scattering orders or an offline solve Accuracy demonstrated against the chosen reference

Use the compact branch for changing cameras and suns. Use the higher-order branch when its regeneration cost and higher-dimensional products fit the actual workload. Keep an offline integrator as reference evidence rather than visible-pixel runtime work.

Complete when: the implementation names one branch, its supported spatial domain, its physical-versus-authored claim, and the error or visual criterion that admits it.

2. Establish one model

Declare one length unit for radii, positions, and integration steps; store extinction/scattering coefficients in its reciprocal unit so beta * ds is dimensionless. Define the body or local frame, origin policy, altitude model, sun direction convention, spectral/working basis, and whether the solar source is normal irradiance or finite-disc radiance.

Initialize WebGPURenderer, await renderer.init(), and confirm the native WebGPU backend before allocating compute/storage products. Use NoColorSpace for transport data and keep radiance scene-linear until the host output stage.

For physically dimensioned extinction, LUT radiometry, mapping equations, phase conventions, product dependencies, imported LUT/product compatibility, or the single unit-equivalence fixture, read references/atmosphere-transport.md.

Complete when: every producer and consumer uses the same frame, length conversion, sun convention, and radiometric basis; physically dimensioned extinction also gives the same fixture optical depth on CPU and GPU.

3. Build products in dependency order

The authored local branch samples its sky and distance/height haze directly from the shared model and host depth. The compact LUT branch generates:

transmittance
  -> multiscatter and optional irradiance
  -> camera/sun sky-view
  -> aerial RGB inscattering plus RGB optical depth
  -> scene composition

Give each product its own dependency key and last-update reason. Atmosphere profile and body changes dirty base products. Camera body-relative pose, projection, viewport, and depth mapping dirty aerial products. Camera yaw, projection jitter, and a pure floating-origin translation leave unchanged body-frame LUTs valid.

Write 2D products with StorageTexture and textureStore(); write 3D products with Storage3DTexture and storageTexture3D(). Set formats, filters, wrapping, and mip ownership explicitly. Treat renderer.computeAsync() as submission, not GPU completion; synchronize readback, reuse, and retirement with an actual completion mechanism.

Complete when: every sampled product names all of its physical and view dependencies, no consumer can observe a newer dependency with an older dependent product, and unchanged base LUTs survive camera-only changes.

4. Compose sky and aerial perspective

Reuse the host scene color and depth. Reconstruct the active perspective, reversed, logarithmic, or orthographic depth convention into a metric segment, then intersect that segment with the atmosphere. Classify sky through explicit coverage or the declared clear-depth encoding.

For a visible surface, apply exactly:

C_out = C_scene * T_segment + S_segment

For a sky pixel, sample sky radiance and the calibrated sun/moon disc. Keep direct lighting, diffuse sky lighting, cloud shadows, opaque visibility, and camera-segment transport as separate factors. Let either renderOutput() or RenderPipeline.outputColorTransform own presentation.

For planetary bodies, exterior cameras, ellipsoids, non-perspective depth, or a shell/post handoff, read references/body-depth-and-composition.md.

Complete when: standard and enabled alternate depth modes reconstruct known positions within the declared tolerance, sky/surface coverage is stable, the zero-atmosphere control returns C_scene, and tone mapping/output conversion runs once.

5. Expose the lighting handoff

Expose only the quantities another system needs:

  • sample time, model revision, physics frame/origin, support, filter, age, and error;
  • sample-to-sun unit direction and disc angular radius;
  • calibrated solar quantity, unit, and basis;
  • either direct sun already attenuated by the atmosphere, or the unattenuated source plus atmosphere transmittance;
  • directional sky radiance, normal-dependent sky irradiance, and whether each includes the direct disc;
  • camera-segment RGB transmittance and RGB inscattering.

Clouds add cloud-only optical depth; geometry adds visibility; water adds its own path extinction. A consumer chooses one atmosphere direct-light form and applies each factor once.

Complete when: every lighting consumer can identify quantity, unit, frame, sample age, and included attenuation, and a factor trace proves that atmosphere, cloud, geometry, water, and aerial transport each appear at most once.

6. Verify the selected branch

Verify the mechanisms that branch actually uses:

  • LUT forward/inverse maps at texel centers, boundaries, horizon split, and azimuth seam;
  • phase normalization and forward-lobe sign;
  • optical-depth, radiance, and energy convergence against a higher-accuracy reference;
  • body intersections and every enabled depth encoding;
  • product invalidation under parameter, camera, jitter, viewport, and origin changes;
  • fixed-view linear-HDR sky, horizon, night, surface-haze, and exterior-camera diagnostics;
  • create, resize/tier-switch, completion, and disposal ownership.

Complete when: every enabled branch passes its numeric and visual gates, every diagnostic names the product revision it displays, and repeated resize/tier-switch cycles leave one live generation per retained product.

Failure signatures

Symptom Inspect
Halo appears opposite the sun phase direction sign
Haze changes with world scale length/coefficient conversion
Horizon seam or limb pop LUT seam, body interval, or owner transition
Terrain is darkened twice direct-light or aerial factor ownership
Camera jitter regenerates base LUTs dependency keys are too broad
Off-axis haze is too short normalized depth was mistaken for ray distance

Routing boundary

This skill owns molecular/aerosol sky transport, atmosphere-derived sun/sky lighting, and camera-segment aerial perspective. Use $threejs-volumetric-clouds for weather-shaped cloud density and cloud-only shadows, $threejs-image-pipeline for shared scene signals and final-output ownership, $threejs-exposure-color-grading for metering/tone mapping, and $threejs-procedural-planets for terrain/body detail.

Secondary provider surfaces

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.