Water Raymarch — Seascape Playground
App: apps/water_raymarch/
This example evolved. It began as a self-contained scene ray-marched inside a single full-screen post (the owner’s Slang Playground port of the gameidea.org stylized-water tutorial). It is now a full interactive seascape playground: that same look is rendered on a real mesh surface, with a free-fly camera, runtime variant + weather toggles, buoyant physics floaters, and the underwater pass — the equivalent of Water Playground, built on the seascape water.
The whole point is fidelity to the reference look on real engine geometry. The mesh surface shader
shaders/water_seascape.slang reproduces the tutorial’s Seascape algorithm faithfully — the
Alekseev |sin|/|cos| octave wave heightfield (3 octaves displacing the vertex, 5 for the
fragment normal), colored Beer-Lambert absorption from a bright shallow tint to a deep teal,
world-projected caustics, layered foam (fbm-Voronoi + wave-crest), and a procedural-sky
reflection with fresnel and a sun glint — and the underwater view reproduces the tutorial’s
underwaterFragmentMain as a full-screen post.
Run from root
Section titled “Run from root”% ./plume3d water_raymarchOn launch it runs a short scripted tour (spawns floaters, shows both variants and a storm, dives under) so a headless run self-demonstrates; press any control key and it hands straight to free-fly.
What it demonstrates
Section titled “What it demonstrates”Everything here rides already-shipped engine capabilities — the mesh
set-5 custom material, the
offscreen post + scene-depth path,
opaque capture, Water buoyancy, and the
Sky/Weather driver — plus one small additive Water method,
setSeascape, so buoyancy tracks the seascape octaves.
- Seascape mesh water. A subdivided
MeshGen.planeis drawn withwater_seascape.slang. The vertex displaces Y by the 3-octave seaOctave sum (centred on the plane so the waterline sits where the still plane is); the fragment does the depth colour, caustics, foam, reflection and sun glint. A second shader,water_toon.slang, gives the flat banded Toon look for the2toggle. - Free-fly, above and below. An explicit camera (yaw/pitch + WASDQE) flies anywhere, including under the surface — where the underwater post + the Snell-window underside take over.
- Underwater that matches the surface.
shaders/underwater.slangis a faithful port of the tutorial’s underwater pass. It decides submersion from a stable camera-vs-still-level mode gate (with a smooth band, so breaching the surface fades instead of popping) and then tints by the same octave wave field the surface uses, so the crossing is wave-accurate and the seabed fogs to the seascape palette rather than showing raw geometry. - Buoyant floaters.
SPACE/Bspawn a crate (4 corner probes → it rocks) or a barrel (single probe → it bobs);Physics.applyBuoyancyruns each against aWaterwhosesetSeascape(true)mirrors the same octave field on the CPU, so they float on the waves you see. Crossing the surface kicks a splash. - Splashes, ripples & precipitation — via the particle system. The water FX now ride the engine’s
built-in particle + instancing systems through
Scene.draw(), not hand-rolled immediate-mode draws. A floater crossing the surface bursts a surface ripple ring (aSpriteGroundparticle — a soft foam ring laid flat on the water that expands and fades) plus a camera-facing splash flash (aSpriteBillboardparticle), both pinned kinematic decals reusing the sprite pipeline (see Particle FX Demo). Rain and snow are real GPU-instanced 3D geometry — twoInstancedMeshstreak volumes (rain / snow chosen per-instance) that theWeatherdriver toggles per state — drawn by the sameScene.draw(), replacing the earlier screen-space precipitation overlay. - Weather + time of day, from the shared packs. The atmosphere is driven by the actual
TimeOfDayandWeathercontent-pack scripts — not a bespoke copy.TimeOfDayowns the day cycle (its own sun, and the sky/ambient/fog when it’s clear);Weathertakes over the overcast sky/fog/wind/precipitation when it isn’t.TABcycles the Weather pack’s four states Clear → Rain → Snow → Storm (precipitation is the GPU-instanced rain/snow above, bundled per state; storm adds lightning), and holding T advances time of day. Overcast states paint the animated cloud sky (shaders/clouds); clear stays theTimeOfDaygradient. The water reads the packs for its own hooks — the wind gust for its swell, the current sky horizon for its reflection tint, and the time-of-day sun for the underwater god-rays.
Controls
Section titled “Controls”- W/A/S/D — move · Q/E — down/up · Arrow keys — look
- SPACE — spawn a crate · B — spawn a barrel · R — clear floaters
- 1 — Seascape variant · 2 — Toon variant · TAB — cycle weather (clear/rain/snow/storm) · hold T — time of day
- Any control key — end the intro tour and take over · ESC — quit
Water—setWaves+setSeascapeselect the seascape octave profile so buoyancy matches the rendered surface;applyBuoyancyfloats the crates and barrels.Graphics.setWaterPlane+addPostEffect— register the still water level for submersion and run the underwater pass.Graphics.setSky/setFog/setWind/setAmbient— the weather presets.Input+Gui— the free-fly controls and the on-screen HUD.- Water Playground — the sibling playground on the engine’s Realistic water; Water Demo — the side-by-side surface reference.
Notes and limitations
Section titled “Notes and limitations”- A clean-room reimplementation. The Seascape algorithm and the underwater pass are reproduced as our own Slang from the public tutorial technique — no third-party source is vendored.
- Buoyancy uses a centred octave mirror. The seaOctave sum is non-negative, so both the shader and
the
Waterheight query subtract a0.9·gainbias to centre the waves on the plane; that is what keeps the floaters, the waterline and the registered still level in agreement. - The underwater pass is camera-gated, by design. The full-screen underwater tint engages from the eye’s depth below the still level (not the wavy height), so a passing crest can’t flicker it and it never overpaints the surface seen from above; the per-pixel wave field still shapes the crossing.
- Surface ripple rings read subtly at this grazing camera. The
SpriteGroundripple lies flat on the water, so from the near-water diver camera it foreshortens to a thin white foam accent rather than a crisp ring; viewed from above (as in Particle FX Demo) the same rings read as clean concentric circles. The camera-facing splash flash is unaffected by angle. - The precipitation volume follows the camera. The rain/snow instances share one XZ offset table that
is re-snapped to a grid cell around the camera each frame (a cheap per-instance
setInstancePosition, one SSBO upload), so the precipitation surrounds the diver wherever they fly — it never clumps at the origin. While it precipitates, the rain also stipples the surface: a foam ripple ring bursts at a random point near the camera every fraction of a second (reusing the splash ripple emitter), so the water reads as actively rained-on.