NatureGL Waterv1.1.0

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.

The demo's Dive: the camera rides the surface, then sinks to about 6.5 m over the reef.

#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, scattering and 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.
js
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

ParamDefaultEffect
underwaterFog0.03Fog 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
caustics1Caustics 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.

js
// murky green harbour water
water.setParams({ underwaterFog: 0.12, absorption: [0.6, 0.2, 0.35], scattering: [0.02, 0.06, 0.03] });
The demo's wreck, at least 5.7 m down. It is drawn in the world pass like any mesh, and the water in front of it is absorbed and refracted by its real thickness from the depth buffer.

#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.

js
water.caustics.patch(seabed.material);
water.caustics.patch(rock.material, { strength: 0.7, scale: 0.15 });
OptionDefault
strength1Per-material multiplier. The global dial is the caustics param
scale0.11Pattern cells per metre
lightAbsorption0.25How 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.