In this tutorial I will give an easy and intuitive explanation on how to code a cloth simulator using Newtons second law, Verlet integration, and iterative constraint satisfaction. I do not expect you to know these terms – or physics for that matter. The tutorial is accompanied by easy-to-read source code in object oriented C++. I have opted for a minimal dependency on external libraries (OpenGL and Glut) and kept all source code in a single cpp file.
Source Code
Source code including Visual Studio 2005 solution and Makefile
Motivation
I wrote my first cloth simulation in 2001 – and it was one of the most gratifying coding experiences I have had. The math involved can be easily understood intuitively, it does not take a long time to code, and the dynamic animation is (to me at least) an impressive emergent property of the very simple rules of particles of mass and interconnecting constraints. Furthermore I have found there to be many interesting applications of related techniques. One such example is the cardiac surgery simulator:
A cloth consists of particles and constraints
The simulation of cloth, is really about the simulation of particles with mass and interconnections, called constraints or springs, between these particles – think of them as fibers in the cloth. Particles move around in space due to forces that affect them – e.g. wind, gravity, or springs between particles.
Particles: from forces to positions
As a first step we would like to be able to affect particles with forces and calculate how much that shifts their position – within some small amount of time. To do that we need to use two pieces of knowledge.
- Force can be “translated” into acceleration through Newtons second law: acceleration = force/mass
- Acceleration can be “translated” into position by numerical integration – since position twice differentiated is equal to acceleration.
Item 1 is easy, and simply translates into the following method (on Particle class):
1 2 3 4 |
void addForce(Vec3 f) { acceleration += f/mass; } |
If item number 2 does not make intuitively sense to you, then think about a car moving along a road. If you plot a graph of the distance traveled over time, the gradient at a given time is the velocity of the car. If you plot the velocity on a graph and take the curvature at a given time you get the acceleration of your car (the change in velocity/speed). To go from acceleration to position we need to do the “inverse” of differentiation, namely integration. The integration routine we will use is called verlet integration. Pure verlet integation looks like this (but notice that we will modify it below to include damping in the source code)
1 2 3 |
Vec3 temp = pos; pos = pos + (pos-old_pos) + acceleration*TIME_STEPSIZE2 old_pos = temp; |
To understand this intuitively I like to think about the problem geometrically. Lets start with a particle that is not moving (pos and old_pos are the same). Lets say that we would like to push the particle by applying some acceleration vector: Intuitively, the particle should move in the direction of the acceleration vector. With that scenario in mind, the above verlet integration formula simply says to offset the current position by the acceleration vector. (The offset is scaled with the time step size to account for the fact that a particle should move a less amount when the piece of time is small, and vice-versa). In our current scenario the consequense is that the current position changes, and the old position stays the same.Now lets assume that we do not affect the particle with acceleration for the next time step. Intuitively, the particle should keep moving in the direction that we pushed it. The is realised through the term (old_pos-pos), a vector from old_pos to pos, in the verlet integration formula – implicitly representing a velocity vector. The next time step would consequently look like this: To get to the final code used in the project we will include some damping (to heuristically account for gradual loss of velocity due to e.g. air resistance). An easy way to do is to simply multiply the velocity vector (old_pos-pos) with an amount between 0 and 1. Closer to zero makes the particle mover slower and slower each time step. An amount closer to one allows the particle to move unrestricted. Since we would like to specify damping, the amount that we will multiply by is (1.0-DAMPING). This writes out like so:
1 |
pos = pos + (pos-old_pos)*(1.0-DAMPING) + acceleration*TIME_STEPSIZE2 |
The final source code for our verlet integration method in the Particle Class is then:
1 2 3 4 5 6 7 8 9 10 |
void timeStep() { if(movable) { Vec3 temp = pos; pos = pos + (pos-old_pos)*(1.0-DAMPING) + acceleration*TIME_STEPSIZE2; old_pos = temp; acceleration = Vec3(0,0,0); // acceleration is reset since it HAS been translated into a change in position (and implicitely into velocity) } } |
Constraints: move particles back into place !
Moving particles around is fine if you just want a simple particle system with no connections between them – but we want more! We would like to constrain the movement of particles, so that they try to stay in a grid. The more “effective” we are at returning all particles to the same relative position, the more rigid the cloth will behave. Notice that it is not an option to simply “reset” all particles to the original positon, since that contradicts our wish to affect the cloth with other forces such as wind and gravity. We will base our connections on constraints of distances between pairs of particles. Hence, each constraint between two particles has a distance that it would like to return to for the cloth to be at rest – we call that distance rest_distance. During the verlet integration, as explained above, particles move around, resulting in particles that are too far away from each other or too near each other. Therefor we introduce constraint satisfaction to modify the position of the two particles so that their distance is once again rest_distance. The following illustrations show two particles too far away from each other: Constraint satisfaction works by moving p1 and p2 along the line connecting them, so that they once again have a distance equal to rest_length. To maintain symmetry we will move p1 and p2 by the same amount (either towards each other, or away from each other to satisfy the constraint). As it turns out, if we find the vector (we call it corrrectionVectorHalf) to move p1 by, we can use that same vector in opposite direction to move p2. Consider the vector from p1 to p2. By itself, it is too long an offset vector to be used on p1 (it would bring it all the way to p2). We will find a factor to multiply this vector with, to allow p1 to satisfy (half) of the constraint. That factor is exactly (1 – rest_distance/current_distance)*0.5. The 0.5 comes from the fact that we move p1 only half the way to satisfy the constraint (p2 moves the other half). The rest of the factor can be interpreted as finding the percentage of the whole vector p1 to p2 that is beyond rest_length – that is, we take 100%, and subtract rest_distance as a percentage of the whole distance. All in all the satisfyConstraint method on the Constraint class looks like this:
1 2 3 4 5 6 7 8 9 |
void satisfyConstraint() { Vec3 p1_to_p2 = p2->getPos()-p1->getPos(); // vector from p1 to p2 float current_distance = p1_to_p2.length(); Vec3 correctionVector = p1_to_p2*(1 - rest_distance/current_distance); Vec3 correctionVectorHalf = correctionVector*0.5; p1->offsetPos(correctionVectorHalf); p2->offsetPos(-correctionVectorHalf); } |
The Cloth Class: Building the cloth
We will connect particles in a certain pattern to make the cloth behave realistically. The cloth should both stay in a grid in the plane of the cloth (structural constraints), resist shearing in the plane of the cloth (shear constraint), and resist bending (bending constraint). Below is illustrated how constraints are constructed for each particle (in red). Notice that the current (red) particle will end up having more constrains than shown since this pattern of constraints is repeated for each particle.In the accompanying source code the build-up of the cloth connections is realized in the Cloth constructor using Particles and Constraints.
The Cloth Class: blowing in the wind, gravity and collision with a ball
To make the cloth have an interesting dynamic behavior we will add wind forces and gravity. We will also handle collision with a moving ball. Adding gravity is very easy, we simply add an acceleration vector pointing down to all particles (through the addForce method on the Cloth object). Collision detection and handling is also relatively easy, and I will not go into much detail. The ball is defined by a center and a radius. Detecting collision is done by asking whether the particle is within the radius of the ball, if yes the collision is resolved by moving the particle out of the ball along the vector from center to particle so that the distance is equal to the radius of the ball. Adding wind is a bit more complicated, but results in a very nice effect. We will start by looking at the cloth as a collection of triangles of three particle each, and solve the problem of adding wind one triangle at a time – adding forces to the three particles the triangle consists of. The wind is coming in a certain direction, but forces should only be added in the direction of the normal of the triangle (or negative normal), and the amount of force affecting the triangle should be proportional to the angle (dot product) between the triangle and the wind. This all boils down to a simple formula for a single triangle:
1 2 3 4 5 6 7 8 9 |
void addWindForcesForTriangle(Particle *p1,Particle *p2,Particle *p3, const Vec3 direction) { Vec3 normal = calcTriangleNormal(p1,p2,p3); Vec3 d = normal.normalized(); Vec3 force = normal*(d.dot(direction)); p1->addForce(force); p2->addForce(force); p3->addForce(force); } |
Conclusion
I hope that you have enjoyed this tutorial and the source code, and that it helped you in some way. Please leave a comment below or email me (clothTutorial@jespermosegaard.dk) – that would mean a lot to me !
Further Reading
I have knowingly skipped a lot of theory, given intuitive explanations instead of complete mathematical proofs, and focused exclusively on a single set of choices for the representation of a cloth simulation. There is a lot more to explore on the subject of physics based animation, and below are a few pointers. My PhD on simulating heart surgery with GPU acceleration spring mass systems Advanced Character Physics by Thomas Jakobsen Cloth Simulation using Mass and Spring System
MNyers
Jun 6, 2009
Really nice tutorial, thx! It would be great to see a sequel about self-collision or cloth tearing.
James George
Jul 29, 2009
hey great little example, very clean.
Just a hint too, if you are on a multicore machine and can use OpenMP, simply multithreading the satisfyConstraints results in massive speed up
#pragma omp parallel for
for(int j=0; j < constraints.size(); j++ )
{
constraints[j].satisfyConstraint();
}
Scoot
Sep 28, 2009
Thx!! It’s really nice!
I ported it to Android, it runs very well.
psb
Nov 14, 2009
Thanks !! You are genius!!
Joshua
Jan 15, 2010
It is a good tutorial. Unfortunately, i can’t open the programming code and try it.
fillo
Apr 1, 2010
Hi! I’ve read this code, it’s really cool. Anyway when i execute it, the animation is really really slow :/ i see about a frame each 2 seconds! do you know how can i improve it and obtain the results that the video show? thank you 🙂
zingy
May 18, 2010
i tried running the code in linux machine but it gave me the following errors:
ClothSim.cpp: In constructor ‘Particle::Particle(Vec3)’:
ClothSim.cpp:137: warning: ‘Particle::acceleration’ will be initialized after
ClothSim.cpp:134: warning: ‘float Particle::mass’
ClothSim.cpp:141: warning: when initialized here
ClothSim.cpp:134: warning: ‘Particle::mass’ will be initialized after
ClothSim.cpp:132: warning: ‘bool Particle::movable’
ClothSim.cpp:141: warning: when initialized here
ClothSim.cpp: In member function ‘void Cloth::drawTriangle(Particle*, Particle*, Particle*, Vec3)’:
ClothSim.cpp:258: warning: taking address of temporary
ClothSim.cpp:261: warning: taking address of temporary
ClothSim.cpp:264: warning: taking address of temporary
ClothSim.cpp: At global scope:
ClothSim.cpp:464: error: ” has incomplete type
ClothSim.cpp:464: error: invalid use of ‘GLvoid’
ClothSim.cpp: In function ‘int main(int, char**)’:
ClothSim.cpp:464: error: too few arguments to function ‘void init()’
ClothSim.cpp:587: error: at this point in file
could u plz suggest me how to make it work?? thanks
王鹏
Jul 8, 2010
Hi, fillo !
You can run in Release mode (not debug mode).
GK
Jul 20, 2010
“If item number 2 does not make intuitively sense to you, then think about a car moving along a road. If you plot a graph of the distance traveled, the curvature at a given time is the velocity of the car.”
A graph of distance with respect to what? Time? Then velocity is the first derivative with respect to time. Curvature is the second derivative with respect to arc length – completely different things.
admin
Oct 12, 2010
GK: you are right. Text corrected. Thanks for pointing it out.
downloader
Oct 4, 2010
this is an awesome tutorial! I’d like to know how to run it as fast as demonstrated in the video 🙁
LG
Oct 20, 2010
Thanks. That really helps.
hazem
Dec 24, 2010
great work … but would you tell me why it is so slow , or could i enhance its speed ..thx 🙂
hazem
Dec 25, 2010
could anyone tell me how i can enhance its speed plz ?
hazem
Dec 27, 2010
I’ve managed to speed it up 🙂
Alireza Sokhandan
Mar 31, 2011
Simple and great
Thanks a lot for this tutorial
safegaard
Apr 5, 2011
Great work. Nice to see another dane on the web, and specialy one like you who are sharing your knowledge like your do.
Tak for det 🙂
waseem
May 4, 2011
hii
thanx about this ,this is very very good
but want to ask you about something
i am studying and in my study make a project online shopping cloth through 3d model
i want to help me by any way i am in level of coding mass spring model to dress the model the cloth how i can start or what i can do
help me plz thanx
Purva Kulkarni
Jun 10, 2019
Hi, It’s been too long. But if you are still interested, I have made a similar project which converts a 2D clothing image into a 3D model using OpenCV and OpenGL. Please check it out->https://www.youtube.com/watch?v=J9FKRR8HsP4&t=20s
Berg
May 18, 2011
Whoa! That’s a cool effect. Thanks for the code. My physics is a bit rusty but I’ll take your word for it on Newton’s law.
herman
May 18, 2011
Hi,
this is a great tutorial but I got a problem here.
How can I simulate a strteching effect such as using two fingers to pull down the cloth?
I tried to add force to it but the results seems not good. Is any tip to simulate this scenario?
Thanks for your help
firebird
May 31, 2011
Thank you very much.
Your article is the best guide about cloth emulation i have ever found.
HRuivo
Jun 9, 2011
Thank you very much, I have been looking for a good tutorial about cloth and yours is definitely the best. Really easy to follow and to understand.
geotavros
Jul 1, 2011
http://www.youtube.com/watch?v=In_WUm1Gsrc
I have successfully ported your algorithm to OpenCL 🙂
NKAID
Sep 8, 2011
Very impressive that tutorial
Anand
Feb 8, 2012
Thanks a lot of this write up.. really helpful
wqs
Mar 19, 2012
hi,
Thanks for you sharing that tutorial,it helps me much.But I have a problem that how to handle cloth self-collision? I hope your can help me,thanks!!!
admin
Apr 19, 2012
The current tutorial does not cover self-collision, and I have not looked into recent results in this area – a brute force method might work for a small enough resolution.
wqs
Mar 19, 2012
hi,
Thanks for you sharing that tutorial,it helps me much.But I have a problem that how to handle cloth self-collision? I hope your can help me,thanks!!!
wqs
jery
Apr 18, 2012
hii,
I’m still studying and doing my project on cloth simulation.
Your tutorial really help me a lot and it’s very impressive. But i would like to know more about the technique your using since i don’t really understand the code and is there any way to speed it up?
can i use other technique like Implicit Midpoint Integration Method and how can i start it or what can i do.
Hope you can help me. Thank you.
admin
Apr 19, 2012
Lot’s of things could speed up the code – if enough people want it, I could do a tutorial on optimization of the cloth code.
ted
Apr 25, 2012
Of course we do 😀
cybercser
Jun 11, 2012
Really intuitive and simple tutorial!! It saves a lot of time for cloth simulation greenhand like me.
ppalasek
Jun 13, 2012
Hello! Thank you for the tutorial!
I just wanted to ask if it is possible that there is an error in your PhD thesis, on page 53, equation (5.6), shouldn’t it be x(t+dt)=(2-lambda)x(t)-(1-lambda)x(t-dt)+x”(t)dt^2 instead of x(t+dt)=x(t-dt)(1-lambda)x(t)lambda
I’m working on a mass spring system and I found your thesis and this website. They are very useful.
Thank you!
Nico
Jul 8, 2012
Very nice tutorial, thank you. It is nice when one is searching for a simple yet effective implementation of something and it appears.
jay
Jul 24, 2012
Thats a very nice tutorial.. Easy to understand,very intuitive ,informative and helpful… Many thanks..!
rsq
Dec 29, 2012
very good i understand it
jiangxu082@gmail.com
Jan 29, 2013
very nice tutorial!
Ian
May 11, 2015
Hi Jesper! This page was very helpful in a course project in which I look into accelerating it with CUDA. You can see my results here:
https://www.andrew.cmu.edu/user/iheath/418/cloth/CUDAClothSimulatorFinalReport.html
Ted
Jun 27, 2015
Great, thank you!
Xincheng Yuan
Apr 29, 2017
Hi, I’m impressed with your nice tutorial. The source code link doesn’t seem to work anymore. Do you still have the source code? I would like to look at it.
Jesper Mosegaard
Jun 8, 2017
Thanks :). The link has been fixed.
Mark
Jul 5, 2017
Thanks a lot!
Have you any suggestion on how to implement collisions with other objects, such as cube, torus, or a cone…? I would appreciate that.
Have a nice day!
Jesper Mosegaard
Aug 3, 2017
You should have a look at literature or introduction to collision handling, those cases are also described
Saman
Jul 28, 2017
Hi, such a great job, thanks Jasper.
Although it is an old source code, I could manage to make it works. It seems that you haven’t written the code based on shaders (to speed up the code using GPU). It would be a good practice for everyone to rewrite the code using the modern OpenGL and its shading language. I really liked the idea of doing everything from the pure code, e.g. adding a texture to the cloth!
Jesper Mosegaard
Aug 3, 2017
Thank you so much. And you are right, the code is before moderne OpenGL. Perhaps one day I should make a WebGL version for easy accessibility 😉 Regards. Jesper
David Escobar Castillejos
Oct 10, 2018
Thanks for the great tutorial Jesper. I would like to know if you could give some advices about how to scalete the concepts and coding you developed but with 3d models. I’m really interested in learning how to apply this tutorial to them (as you did with you surgery simulator). Sincerely, David.
sharun
Aug 8, 2017
really nice turorial
Organism Documentation – Allan's Research Blog
Feb 13, 2018
[…] I’ve completed the code that generates nodes and binds them together via muscles or “constraints”. The code that runs the physics simulation is adapted from a cloth simulation program written in C++ provided by Jesper Mosegaard at https://viscomp.alexandra.dk/?p=147. […]
Victor
Apr 16, 2018
Thanks! I was wondering how to apply forces as vec3
Sotiris Salloumis
Jul 21, 2018
Thanks for sharing, well written tutorial and source code of excellent quality ! Worked fine also on Mac OS. Congrats keep up sharing !
Javier corra
Nov 6, 2018
Thank you for this great article!!, I just found an implementation of this in the ThreeJS library and I thought it would be impossible for me to understand the inner workings but I got it thanks to you!
Marcio Barcellos
Jul 9, 2019
Thank you for sharing!