GPU mass-spring cloth
A cloth that waves from a sine wave looks fine until something touches it. A mass-spring grid on the GPU has 4,096 nodes, each pulled by up to 8 neighbours, so a ball shoves it out of the way.
The problem
Most background flags and curtains are animated in the vertex shader: a few sine waves, no state. It is cheap and loops forever, but nothing can push it. A ball goes straight through.
How it works
The source runs a mass-spring cloth in Unity compute shaders:
- A grid of nodes (64 x 64 here). Each node has a position, a velocity and a mass: total cloth mass divided by node count.
- Each node feels gravity plus 8 springs: 4 to its direct neighbours and 4 diagonal. Spring force is stiffness times the stretch past rest length, plus a damping term from the neighbours' relative velocity along the spring.
- An external force adds a constant acceleration to every node.
- Two nodes on the top row are pinned.
- Each update advances 0.01 s in many small substeps, one GPU pass per substep over all nodes.
What I hit
The position update in the repo is labelled like a Runge-Kutta step, but works out to about velocity / 6 per step, so every node would move at a sixth of its speed. I use the new velocity times the time step instead.
The repo's collision kernel at this commit only copies buffers; its floor test is commented out. The ball push-out in the video is my addition: move any node inside the sphere to its surface and drop the inward velocity. The script still has a helper for the sphere's offset, which I reused.
The pins are node 1 and the last node of the top row, not node 0. The top-left corner hangs loose; I kept that.
My build
three.js (r187), WebGL2. Positions and velocities live in two 64 x 64 float textures; each substep is one fragment pass writing both. Settings: 4 kg cloth, 2.2 m square, stiffness 2400, damping 0.6, 40 substeps per 0.01 s, so 133 passes per video frame. The scene file stores the repo's values in binary, so these are my picks. The sheet starts flat and drops.
Trade-offs
- Real response to pushes and forces, on the GPU
- Simple: one force pass per substep, no solver
- Explicit springs need tiny steps to stay stable
- Stretchy under its own weight unless the stiffness is high
- No self-collision; folds can pass through each other
- Rendered on a software GPU, so no timings
When to use it
Flags, curtains, banners and tablecloths that a player or object can touch. Keep sine-wave cloth for the ones far away.
Source and credits
Technique source: GPU-Cloth-Simulation by JUSTIVE, https://github.com/JUSTIVE/GPU-Cloth-Simulation, MIT license, commit 8484f2d14bf83a441f3f46375343116461a391a6. Ported to three.js and GLSL from its node update compute shader and simulation script: per-node mass from total cloth mass, gravity, 8 springs per node (4 structural, 4 diagonal) with spring and damping forces, the external force term, the two pinned nodes and the fixed 0.01 s update split into substeps. The sphere offset formula comes from the script. No files were copied unchanged.
Render framework: three.js by mrdoob and contributors, https://github.com/mrdoob/three.js, MIT license, branch dev at commit fa01dd6ad164c48554f1bb9456f6500db052ec4f. Used for rendering, shadows and float render targets. It is not the source of the cloth technique.
My own code: the semi-implicit position step, the ball collision, the sine-wave comparison cloth, the stretch-coloured grid view, the scene, labels and video. No external models or assets. No IA model.