Skip to content

Water material

<WaterMaterial> is the material of the mesh it sits in, inside a World. It draws the mesh as water: a lake, a river or a sea. Two layers of noise drift across each other as waves. The water turns lighter in the shallows near its edge and breaks into foam at the shore. Where the view grazes the surface, it catches the sky. The waves run on the world’s clock, as every Shader does, so a paused game holds them still.

Its props are in WaterMaterialProps.

The shore is the edge of the plane’s texture coordinates: the square, or the circle inside it when round is on. Draw the water on a planeGeometry, because another mesh’s edge does not follow that square, and part of its rim would have no foam. Lay the plane flat where the water should be, a little above the ground, and size it to the lake or the river. The waves keep the same size on any plane, because they follow the ground rather than the plane’s corners.

The water draws in one pass, without a depth texture or a second render of the scene, so a phone can afford it. Its sky is a fixed colour, not a true reflection: trees and the sun do not mirror in it.

Roblox makes water a terrain material, with wave size and speed set on the terrain. Unity’s HD pipeline ships a water system, and Godot leaves water to community shaders. Three’s own Water draws the scene a second time for its reflection, which costs a phone a frame’s worth of work. The engine has no terrain to paint, so its water is a material with props, as the CRT material is.

The shader’s source is waterFragment. It is an exported GLSL string with a comment per block: copy it, change a block, such as the sky colour or the foam, and pass it to a Shader for water of your own.

A round pond in the meadow:

import { Entity, WaterMaterial, World } from "@daniel-zarinski/engine";
export function Pond() {
return (
<World map="meadow">
<Entity position={[0, 0.05, -4]}>
<mesh rotation={[-Math.PI / 2, 0, 0]}>
<planeGeometry args={[6, 6]} />
<WaterMaterial round waveSize={1.5} />
</mesh>
</Entity>
</World>
);
}

Client. The water changes nothing in the world; each player’s browser draws its own. A player walks over it as over the ground: it has no collider, and no swimming.