Vertex displacement
Move a mesh's vertices with waves or noise, and recompute the normals so the light follows.
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 shaderThe GPU program that runs once for every vertex of a mesh. It places the vertex on screen by writing gl_Position, and passes values on to the fragment shader. Vertex shader The GPU program that runs once for every vertex of a mesh. It places the vertex on screen by writing gl_Position, and passes values on to the fragment shader.
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 normalA unit vector perpendicular to a surface, pointing the way it faces. Lighting and Fresnel effects compare it with other directions. Normal A unit vector perpendicular to a surface, pointing the way it faces. Lighting and Fresnel effects compare it with other directions.
- 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 matrixA grid of numbers (mat2, mat3, mat4) that maps one vector to another in a single multiplication. Rotation, scale and, with homogeneous coordinates, translation and projection are all matrices. Matrix A grid of numbers (mat2, mat3, mat4) that maps one vector to another in a single multiplication. Rotation, scale and, with homogeneous coordinates, translation and projection are all matrices.rotate(a) = [cos a −sin a; sin a cos a]
Further reading
- Improving Noise (opens in a new tab)Paper
- Perlin noise (opens in a new tab)Reference
- Simplex noise (opens in a new tab)Reference
- Coordinate Systems (opens in a new tab)Article
- WebGL model view projection (opens in a new tab)Docs
- Homogeneous coordinates (opens in a new tab)Reference
- Normal (geometry) (opens in a new tab)Reference
- Basic Lighting (opens in a new tab)Article
- Normal Mapping (opens in a new tab)Article
- sin (opens in a new tab)Reference
- Sine wave (opens in a new tab)Reference
- Shaping functions (opens in a new tab)Book
- The OpenGL ES Shading Language, version 3.00 (opens in a new tab)Docs
- Spatial shaders (opens in a new tab)Docs
- Effective Water Simulation from Physical Models (GPU Gems, chapter 1) (opens in a new tab)Book
- Trochoidal wave (opens in a new tab)Reference
- Dispersion (water waves) (opens in a new tab)Reference
- cross (opens in a new tab)Reference
- normalize (opens in a new tab)Reference
- Your first 3D shader (opens in a new tab)Docs