Skip to content
Distort · TechniqueIntermediate

Vertex displacement

Move a mesh's vertices with waves or noise, and recompute the normals so the light follows.

PositionNormalViewTimeVertex

Cards

One live example to tweak, learn and copy.

How it works

The other Distort techniques fake movement by reading a picture somewhere else. Vertex displacement really moves the surface. It runs in the vertex shader, once per vertex, before the vertex is projected: take the vertex’s position, add an offset, and hand the moved position on.

vec3 moved = position + normal * height(position.xz, time);

The silhouette changes, the surface hides what’s behind it, and the depth buffer sees the real shape. No warp of the UV can do that.

Three things that decide whether it works

The mesh needs vertices to move. The shader can only move the points that exist. A quad has four; a wave across it needs many per wavelength, or the crests show as straight segments. Displacement goes with a subdivided plane, a dense sphere, or tessellation where the platform has it.

Recompute the normal. The vertex keeps the normal of the flat mesh unless you change it. Lighting reads that normal, so a displaced surface lit with the old one looks flat: the shape moves, the shading doesn’t. Two ways to get the new one:

  • Analytically, from the derivatives of the displacement. The surface’s two tangents are the derivatives of the moved position along x and z, and their cross product is the normal. Exact and cheap when the function is known, as for sines and Gerstner waves.
  • By finite differences: evaluate the height a small step away along x and along z, and build the tangents from the differences. It works for any function, noise included, and costs two more evaluations.

Do the same work wherever the surface is needed. The shadow pass, the depth prepass and the physics that floats a boat all need the moved surface. In an engine, the vertex function runs for each pass automatically; a CPU-side buoyancy check must run the same math in the game’s code.

In which space

The offset is usually computed in world space, so that waves line up across neighbouring meshes and don’t rotate with the object. The model matrix takes the rest position into world space first, the displacement is added there, and the view and projection matrices then run as usual.

Further reading