NatureGL Waterv1.1.0

Start here

Quick start

An ocean with the built-in sky and a row of floating boxes in an ordinary three.js scene. This is examples/basic/, step by step.

#Set up the renderer

water.render() does its own tone mapping (ACES by default), so leave renderer.toneMapping at its default, NoToneMapping.

js
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { WaterSystem } from 'naturegl-water';

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

#Build your scene as usual

NatureGL Water works in metres, with sea level at y = 0. Give the camera a far plane that reaches the horizon.

js
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(55, innerWidth / innerHeight, 0.3, 12000);
camera.position.set(0, 7, 26);
const controls = new OrbitControls(camera, renderer.domElement);
controls.maxPolarAngle = Math.PI * 0.49;

#Create the water

create() adds the ocean mesh and the built-in sky dome to your scene.

js
const water = await WaterSystem.create({
  renderer, scene, camera,
  quality: 'high',       // low | medium | high | ultra
  preset: 'tropical',
});

#Add lights that follow the sky

bindLights points a DirectionalLight at the current key light (the sun by day, the moon at night) and colours a HemisphereLight from the sky. Call it every frame.

js
const sun = new THREE.DirectionalLight();
const hemi = new THREE.HemisphereLight();
scene.add(sun, sun.target, hemi);

#Float some objects

One probe makes an object bob like a buoy. size places four probes (bow, stern, port, starboard), so the object also pitches and rolls.

js
const colors = [0xd9534f, 0xf0ad4e, 0x5bc0de, 0x5cb85c, 0xeeeeee];
for (let i = 0; i < 5; i++) {
  const size = 1.5 + i * 0.6;
  const box = new THREE.Mesh(
    new THREE.BoxGeometry(size, size * 0.6, size * 1.6),
    new THREE.MeshStandardMaterial({ color: colors[i], roughness: 0.6 }),
  );
  box.position.set((i - 2) * 6, 0, Math.sin(i) * 5);
  scene.add(box);
  water.buoyancy.addObject(box, i === 0
    ? { draft: 0.2 }                                                 // single probe
    : { size: { length: size * 1.6, beam: size }, draft: size * 0.15 }); // four probes
}

#Update, then render

Call update(dt) once per frame after you move your objects, then render(). It draws your scene into an HDR target with depth, puts the water over it, adds bloom and tone-maps to the canvas. It also picks up canvas size changes on its own.

js
const timer = new THREE.Timer();
renderer.setAnimationLoop(t => {
  timer.update(t);
  controls.update();
  water.update(timer.getDelta());
  water.bindLights(sun, hemi);
  water.render();
});

addEventListener('resize', () => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
});

#Change the sea

js
water.loadPreset('storm');                          // eases over about 1.5 s
water.loadPreset('sunset', { instant: true });      // snap
water.setParams({ wind: 12, time: 18.2 });          // a few values, eased
water.setParams({ clouds: 0.8 }, { instant: true });
water.stats.significantWaveHeight;                  // metres, for a HUD

#Add a coastline

If your scene has terrain, give the water its height function. Waves then shoal over the shallows and break on the beach.

js
water.setBathymetry({
  heightAt: (x, z) => terrainHeight(x, z),   // > 0 is land, sea level is 0
  bounds: { minX: -400, minZ: -400, maxX: 400, maxZ: 400 },
});
The demo island's beach with a baked bathymetry: breakers, swash foam and the reef shelf seen through the water.

#Keep going