Guides
Composing with other packs
The water renders into a scene you own and takes its light from whatever sky you give it. NatureGL Sky plugs in with one call, NatureGL Weather's particles go on the overlay layer, and NatureGL Island builds a whole game on the combination.
#The contract
- The ocean is a mesh in your scene.
create()addswater.oceanand, unless you plug in another sky, the built-in dome.dispose()removes both. - Offscreen work happens in
update(dt). Waves, the FFT passes, the foam simulation, buoyancy and wakes all run there. render()owns the depth pipeline. Your scene goes into an HDR target with depth, the water draws over it, and the composite tone-maps.renderHDR()stops before bloom and tone mapping for other post chains.- The sky is any object with a sun.
setSky()reads it everyupdate(), so a live, animated sky works.
#With NatureGL Sky
import { SkySystem } from 'naturegl-sky';
import { WaterSystem } from 'naturegl-water';
const sky = await SkySystem.create({
renderer, scene, camera,
maxExposure: 2.4, nightExposure: 5, // keep moonlit nights dark
});
const water = await WaterSystem.create({ renderer, scene, camera, sky }); // or water.setSky(sky)
// the sky swaps its env and shadow textures on quality changes: re-plug
sky.addEventListener('quality', () => water.setSky(sky));
renderer.setAnimationLoop(() => {
sky.update(dt);
water.update(dt);
water.render(); // tone-maps itself, matched to sky.displayExposure
});The water reads the sky's sun, moon (color × intensity), ambient.color, envMap, cloudShadow, fogColor and, from NatureGL Sky 1.1, displayExposure. The ocean then reflects the live clouds, cloud shadows sweep across the water, and the sea and sky agree on brightness at every hour. Light the rest of your scene with sky.bindLights(sun, hemi) or water.bindLights(sun, hemi); both follow the same sun.
#Exposure
With syncExposure on (the default) and a sky that has a numeric displayExposure, render() tone-maps with sky.displayExposure × params.exposure. The water's exposure param becomes a multiplier on the sky's metered exposure, and water.exposure reads back the result. Set maxExposure and nightExposure on the sky, not on the water, to keep nights dark.
#Water and the sky's post pass
For NatureGL Sky's height fog, god rays, clouds over geometry and bloom, replace water.render() with renderHDR() and the post pass's composite():
renderer.toneMapping = THREE.ACESFilmicToneMapping; // the sky post pass uses the renderer's tone mapping
const post = sky.createPostPass();
renderer.setAnimationLoop(() => {
sky.update(dt);
water.update(dt);
const hdr = water.renderHDR(); // scene + ocean, linear HDR + depth
post.composite(hdr.texture, hdr.depthTexture, camera); // fog, god rays, clouds over, bloom
});The water still fogs itself with fogDensity. Lower it, or create the water with manageFog: false, if the sky's fog is enough. NatureGL Island switches back to water.render() while the camera is under water, because the composite owns the underwater grade.
#Any sky-like object
setSky(sky, options?) accepts any object shaped like this. Only sun is required.
interface SkyLike {
sun: { direction: THREE.Vector3; color: THREE.Color; intensity?: number };
moon?: { direction: THREE.Vector3; color?: THREE.Color; intensity?: number }; // key light when the sun is down
envMap?: THREE.Texture; // equirectangular radiance for reflections
cloudShadow?: { texture: THREE.Texture; matrix: THREE.Matrix4 }; // matrix: world position -> uv
fogColor?: THREE.Color; // horizon haze
ambient?: THREE.Color
| { sky: THREE.Color; ground?: THREE.Color }
| { color: THREE.Color; skyColor: THREE.Color; groundColor: THREE.Color; intensity: number };
displayExposure?: number; // NatureGL Sky >= 1.1
}| Option | Default | |
|---|---|---|
sunIntensityScale | 1 | The water's key light is sun.color × sun.intensity × this |
moonIntensityScale | 1 | 1.1 At night: moon.color × moon.intensity × this |
moonTint | — | 1.1 A tint multiplied into the moonlight, for example 0x8ca6ff |
envIntensity | 1 | Multiplies the envMap reflections |
syncExposure | true | 1.1 Tone-map with sky.displayExposure × params.exposure |
- Ambient is read from a Color,
ambient.sky,ambient.colororambient.skyColor × ambient.intensity, in that order. Without any, it'sfogColor × 0.6. - Moon. When the sun is below the horizon the key light switches to the moon, faded in over the first degrees of dusk. Skies without
moon.intensityget a fixed dim blue moonlight. - Background. With an external sky the scene background is yours, for example
scene.background = sky.envMap. NatureGL Sky adds its own backdrop mesh.
examples/external-sky/ builds a sky object by hand: a canvas-gradient equirect env map, a fixed low sun and a scrolling cloud-shadow texture.
const sky = {
sun: { direction: sunDir, color: new THREE.Color(1.0, 0.72, 0.48), intensity: 2.2 },
envMap: makeEquirect(sunDir),
fogColor: new THREE.Color(0.85, 0.62, 0.45),
ambient: new THREE.Color(0.25, 0.3, 0.45),
cloudShadow: { texture: makeCloudShadow(), matrix: new THREE.Matrix4() },
};
scene.background = sky.envMap;
water.setSky(sky);#With NatureGL Weather and NatureGL Island
| Library | How it fits |
|---|---|
| NatureGL Weather | Create it on the overlay layer, WeatherSystem.create({ renderer, scene, camera, layer: water.overlayLayer }), so rain, splashes and snow draw over the water. Set weather.fog.sceneFog = false, because the water manages scene.fog. Call water.render(), not weather.render() |
| NatureGL Island | Uses all of it: one SkySystem plugged in with setSky, the terrain as bathymetry, caustics on the seabed, a buoyant boat with a wake, and the sky post pass over renderHDR() |
| NatureGL Sky | The light source, above |