Guides
Underwater and caustics
Move the camera below the surface and render() switches to the view from below on its own. It's the same scene and the same ocean, so you can dive from a boat to the seabed without loading anything.
#Nothing to switch on
water.render() checks where the camera is on every frame. Below the surface it adds:
- Fog and absorption of your scene by distance, from
underwaterFog,absorption,scatteringand the current light. - Light shafts through the surface (
quality.godRaySteps: 6, 10 or 16 samples; off on low). - The surface from below: Snell's window with the refracted sky and your scene, exact dielectric Fresnel, and total internal reflection of the lit water column outside the window.
- The waterline. When the camera straddles the surface, the image is split per pixel on the near plane, with a meniscus line and a gentle lens wobble.
water.underwater; // true while the camera is below the surface this frame
water.underwaterAmount; // 0..1, smoothed: fade audio or UI with it
water.isUnderwater(diver); // test any Object3D#Tune the water column
| Param | Default | Effect |
|---|---|---|
underwaterFog | 0.03 | Fog density under water, per metre. The demo's HUD shows visibility as 3 / underwaterFog (100 m by default) |
absorption | [0.42, 0.075, 0.05] | Beer–Lambert absorption per metre, RGB. Red goes first, so deep water turns blue-green |
scattering | [0.004, 0.05, 0.085] | In-scatter per metre, RGB. Sets the colour of the water itself |
caustics | 1 | Caustics on patched materials and in the light shafts |
The same absorption and scattering colour the water from above: shallow water over sand looks turquoise because only a few metres of red are absorbed.
// murky green harbour water
water.setParams({ underwaterFog: 0.12, absorption: [0.6, 0.2, 0.35], scattering: [0.02, 0.06, 0.03] });#Caustics on your materials
water.caustics.patch(material) adds animated caustics to any built-in lit material: Standard, Physical, Lambert, Phong or Toon. It chains an existing onBeforeCompile.
water.caustics.patch(seabed.material);
water.caustics.patch(rock.material, { strength: 0.7, scale: 0.15 });| Option | Default | |
|---|---|---|
strength | 1 | Per-material multiplier. The global dial is the caustics param |
scale | 0.11 | Pattern cells per metre |
lightAbsorption | 0.25 | How strongly sunlight reddens and dims on the way down, times absorption |
The pattern only appears below sea level. It's projected along the refracted sun, fades with depth and on down-facing surfaces, and modulates direct sunlight only, so shadows stay dark.
For your own shaders, CAUSTIC_FN_GLSL provides caustic(uv, t) and causticRGB(worldPos, refrSun, t, scale). The refracted sun direction is water.uniforms.uRefrSun.
#Before and after
The original page rendered the reef as a separate scene. The packaged version dives under the ocean you were looking at.