#Author water like you author geometry
Give addRiver() a centre line whose y is the water level, then add a lake with addLake() and a cascade with addWaterfall(). carveTerrain() cuts the channel, the basin and the plunge pool into your own height function, so the ground and the water always agree.
golden preset: a brook fills the upper lake, the outlet runs to the lip, and the cascade drops 3 m into the plunge pool.- body types: river, lake, waterfall
- 3
- presets, one file each
- 9
- quality tiers, low to ultra
- 4
- display cap on high, Apple M5, 1280×720
- 120 fps
#What's in the box
- Spline rivers
A centripetal Catmull-Rom centre line with per-point width and depth. Narrow sections run faster, and steep ones turn into rapids.
- Waterfalls
A ballistic sheet with a glassy lip and an aerated curtain, an outer veil, spray, mist, a boiling plunge pool and ripple rings.
- Depth-true water
Beer–Lambert absorption and in-scattering along the real thickness of water, read from the scene's depth buffer.
- Caustics and wet rocks
patchMaterial()adds depth-focused caustics, a lapping wet band at the waterline and splash zones to your own materials. - Flow-mapped detail
Ripples and foam are advected in each river's own flow space. Foam streaks stretch along the current.
- Terrain that matches
carveTerrain()wraps any(x, z) => heightfunction and returns one with the beds, basins and pools cut in.
#Nine looks, ready to load
Each preset is one file in src/config/presets/ with a water block and a light block. Switch with loadPreset(), which eases the change in, or edit single values with setParam().
clearClear mountain stream at noon: turquoise over pebbles, strong caustics.goldenLong light and warm glints on the ripples.glacialMilky turquoise rock-flour water, faster and foamier.muddyBrown, opaque and fast, with heavy foam and almost no caustics.swampTannin-dark, slow, glassy water under a greenish haze.nightMoonlit: stars, dim moon glints and dark water.#A river, a lake and a waterfall in one screen of code
import * as THREE from 'three';
import { RiverSystem } from 'naturegl-river';
const water = await RiverSystem.create({ renderer, scene, camera, preset: 'clear' });
water.addLake({ center: new THREE.Vector3(0, 3, -24), radius: 9 });
water.addWaterfall({ top: new THREE.Vector3(0, 3, -11), bottom: new THREE.Vector3(0, 0, -9.8), width: 5 });
water.addRiver({
path: [-9.8, -2, 6, 14, 24].map((z, i) => new THREE.Vector3(Math.sin(i * 1.3) * 4, 0, z)),
width: 7, depth: 1.6, flowSpeed: 1.6,
});
const height = water.carveTerrain((x, z) => 0.6 + Math.max(0, Math.abs(x) - 5) * 0.2);
renderer.setAnimationLoop(() => {
water.update(timer.getDelta());
water.render(); // replaces renderer.render(scene, camera)
});#Light that reaches the bed
clear preset at noon, close to the bank. Caustics focus with depth and animate faster where the water runs faster. The wet band on the stones laps with the ripples.#Part of the NatureGL series
NatureGL River takes its sun, reflections and cloud shadows from any sky-like object through setSky(), including NatureGL Sky's SkySystem. Its height and water queries are plain functions, so NatureGL Grass can grow along the banks and stop at the water.