b3d-clouds

A cloud layer you can fly into — and lose the world inside.

Not a skybox texture and not a shader: a few dozen opaque blobs at an altitude, plus a fog whiteout that goes total before you reach one. Fly into a cloud and the world is already gone; you're in white until you come out the far side.

It's a TACTIC, not a texture

insideCloud (0…1) is exposed, so a cloud is something the game can use: break a radar lock, shake a pursuer, hide a mothership until you're on top of it. That is the whole point — behavioural richness, not photorealism (see AI-DESIGN.md). A cloud you can hide in is worth more than a cloud that merely looks convincing.

Demo

Climb into the cloud layer (R to throttle up, W/S pitch). Watch the world white out as you enter, and the readout climb. Then dive back out.

import { b3d, b3dAircraft, b3dClouds, b3dFog, b3dLibrary, b3dLight, b3dSun, b3dSkybox, b3dGround, gameController, inputFocus } from 'tosijs-3d'
import { elements } from 'tosijs'
const { div } = elements

const aircraft = b3dAircraft({
  library: 'vehicles', meshName: 'scout',
  player: true, y: 40, vtolSpeed: 6, maxSpeed: 60,
})
const clouds = b3dClouds({ altitude: 140, thickness: 40, count: 40, size: 70, castShadows: true, seed: 7 })
const readout = div({ class: 'readout' })

const scene = b3d(
  { gamepad: true },
  b3dLight({ y: 1, intensity: 0.6 }),
  b3dSun({ intensity: 0.9 }),
  b3dSkybox({ timeOfDay: 10 }),
  b3dFog({ start: 400, end: 3000, color: '#bfd9f2' }),
  b3dGround({ meshName: 'ground_nocast', width: 4000, height: 4000, color: '#6b7f5e' }),
  b3dLibrary({ url: '/test-2.glb', type: 'vehicles' }),
  clouds,
  inputFocus(gameController(), aircraft),
)

setInterval(() => {
  const t = clouds.insideCloud
  readout.textContent = `altitude ${aircraft.altitude.toFixed(0)}   in cloud ${(t * 100).toFixed(0)}%`
  readout.style.color = t > 0.5 ? '#fff' : '#9fb'
}, 100)

preview.append(scene, readout)
tosi-b3d { width: 100%; height: 100%; }
.readout {
  position: absolute; bottom: 12px; left: 12px;
  padding: 6px 12px; border-radius: 6px;
  background: rgba(0,0,0,0.55); color: #9fb;
  font: 13px ui-monospace, monospace; z-index: 10;
}

How it works

Attributes

Attribute Default Description
count 36 Blob POOL size (fixed — they recycle, they don't grow). coverage decides how many are active
altitude 140 Centre height of the layer
thickness 36 Vertical spread of the layer
spread 1200 Radius of the disc of cloud around you
size 70 Blob radius — also the distance at which whiteout is total. Set at build
color '#ffffff' Cloud (and whiteout) colour
opacity 1 Blob alpha. Default OPAQUE — translucent clouds read badly. Set < 1 only for deliberate wisps
fogDensity 1.0 Whiteout density (EXP2, the scene's mode). HIGH on purpose — inside a cloud you should see nothing but white within a few metres
approach 0.5 Where the whiteout BEGINS, × size outside the blob. It's TOTAL well before the surface (you never see the geometry edge) and stays total until you leave
selfIllum 0.35 Self-illumination 0…1 — 1 ≈ fully self-lit (old look), 0 = only sun-lit (dark undersides)
coverage 0.5 Weather dial 0…1: wisps → overcast/thunderheads. LIVE. Gates active count + opacity + darkness + self-illum
castShadows false Opt-in: project the field's shadows onto every shadow-receiving surface (cloud-shadows)
shadowStrength 0.65 Darkness of those shadows 0…1, scaled further by coverage (≈50% darkening at typical coverage)
seed 1 Deterministic layout — same seed, same sky