Controls: Space to pause, S to toggle sphere movement, R to reset.
Click and drag to rotate; hold shift to pan, alt to zoom.
A quick weekend project for me to learn WebGPU.
This was inspired by a homework assignment in CSCE 451 — Computer Animation at Texas A&M taught by Dr. Shinjiro Sueda.
Fig. 1: A cloth can be represented in this framework using discrete particles connected via springs.
In our case, we have springs that connect to their horizontal, vertical, and diagonal neighbours
Additionally, there are springs that skip across or vertically, these are called "bending" springs.
If we have NM particles assembled in a N×M grid that are connected using the scheme in Fig. 1, then we expect to have 6NM−5(N+M)+2∈O(NM) constraints that we need to solve.
(Specifically, we have N(M−1)+M(N−1) horizontal and vertical springs, 2(N−1)(M−1) diagonal springs, N(M−2)+M(N−2) bending springs.)
There are multiple ways we can solve this system of constraints. One way is to set up a linear system Ax˙=b(see my notes from when I took this course in 2022) and using some solver to find the updated velocity x˙, however, this is slow with solvers like LDLT taking at worst O(n3) – typically n2 – time with n∈O(NM). Another way is to use XPBD as described in Alg. 1 below, from which we can easily observe that it takes O(NM) time.
XPBD
# Input:
# - Simulation timestep h, and gravity g.
# - Particles with position pi, predicted position xi, velocity vi, mass mi>0, and damping coefficient di.
# - Constraints (springs) which connect particles j and k, a rest length Li, compliance (or reciprocal of spring constant) αi, the accumulated Lagrange multiplier λi.
# Integrate
for all particles ido
fi←mig−divi# Sum forces acting on particle i (gravity + damping)
vi←vi+hmi−1fi# Integrate force
pi←xi
xi←xi+hvi# Integrate velocity
# Solve Constraints
for all spring constraints ido
λi← 0
forpass∈{0,…,N}do
for all spring constraints i connecting particles j and kdo
# Compute Constraint
Δxi←xj−xk
Ci←∥Δxi∥−Li
∇Ci←∥Δxi∥Δxi
α~←αh−2
# Compute Lagrange multiplier
Δλi←−(Ci+λiα~) / (mj−1+mk−1+α~)
λi←λi+Δλi
# Apply accumulated impulse
xj←xj−λimj−1∇Ci
xk←xk+λimk−1∇Ci
# Update
for all particles ido
vi←h−1(xi−pi)
The loops involving particles are trivially parallelizable, as the integration and update steps only depend on the properties of the current particle.
Special consideration needs to be taken for solving the constraints, because XPBD uses the Gauss-Seidel method which immediately uses the values in each iteration.
This is an issue because if we naïvely try to solve all the constraints in parallel then we will have resource contention due to conflicting reads and writes to a particle's predicted position.
One way to solve this is to use graph coloring; we can construct a conflict graph using each constraint as a vertex in the graph and constraints which affect the same particle are connected via an edge.
Because this cloth does not change in topology, we can do greedy graph coloring when we construct the constraints.
For each vertex, we maintain a set of colors used by the constraints connected to it.
Then, whenever we add a new constraint we take the union of the color sets of each particle connected to it, and we assign the constraint the first unused color.
(For the graphs generated using springs as defined in Fig. 1, this greedy coloring finds a Δ−1 coloring where Δ=12 for N,M≥5.)
Then it's a simple modification to the constraint solver loop in the XPBD algorithm:
# Solve Constraints
for all spring constraints ido
λi← 0
γi←GraphColor(i)
forpass∈{0,…,N}do
for all graph colors γdo
for all spring constraints i connecting particles j and kdo
ifγi<>γthen
continue
# Otherwise, solve constraints as described in Alg. 1
Now, all of the loops in XPBD can be trivially written with compute shaders.
The details behind the WebGPU implementation and WGSL shaders aren't particularly interesting; it is pretty straight-forward with setting up the relevant buffers, bindings, and pipelines.