Welcome back to another post about physics and this time I will be talking about Soft Body Dynamics. Soft Body Dynamics can be done in several approaches, the one I am going to discuss here is the Mass-Spring approach. Just like last time I’m going to take a look at what wikipedia has to say about these 2 topics:

About Soft Body Dynamics:

Soft body dynamicsis a field of computer graphics that focuses on visually realistic physical simulations of the motion and properties of deformable objects (orsoft bodies).^{[1]}The applications are mostly in video games and film. Unlike in simulation of rigid bodies, the shape of soft bodies can change, meaning that the relative distance of two points on the object is not fixed. While the relative distances of points are not fixed, the body is expected to retain its shape to some degree (unlike a fluid). The scope of soft body dynamics is quite broad, including simulation of soft organic materials such as muscle, fat, hair and vegetation, as well as other deformable materials such as clothing and fabric. Generally, these methods only provide visually plausible emulations rather than accurate scientific/engineering simulations, though there is some crossover with scientific methods, particularly in the case of finite element simulations. Several physics engines currently provide software for soft-body simulation.

About Mass-Springs:

In this approach, the body is modeled as a set of point masses (nodes) connected by ideal weightless elastic springs obeying some variant of Hooke’s law. The nodes may either derive from the edges of a two-dimensional polygonal mesh representation of the surface of the object, or from a three-dimensional network of nodes and edges modeling the internal structure of the object (or even a one-dimensional system of links, if for example a rope or hair strand is being simulated). Additional springs between nodes can be added, or the force law of the springs modified, to achieve desired effects. Applying Newton’s second law to the point masses including the forces applied by the springs and any external forces (due to contact, gravity, air resistance, wind, and so on) gives a system of differential equations for the motion of the nodes, which is solved by standard numerical schemes for solving ODEs.

^{[8]}Rendering of a three-dimensional mass-spring lattice is often done using free form deformation,^{[9]}in which the rendered mesh is embedded in the lattice and distorted to conform to the shape of the lattice as it evolves.

I think the information provided by Wikipedia is enough in itself, but just to be sure I will be telling how I have it in my head and how I did the implementation: I simply used my sphere class and expanded it so it has a mass and a force, both are needed to simulate a proper example.

To define a shape, I will be using the spheres. Obviously, the spheres must have some connection between them or else it would just fall apart. The connection between the spheres will be a spring. I don’t think I have to explain what a spring is, but I will give a short explanation about the properties we will be using in our spring to make the simulation.

The spring class I made is very small (you could just make it a struct really) and just holds 3 variables:

- The rest length.
- The spring stiffness.
- The damping constant.

The rest length is the length the spring wants to return to when you stretch or compress the spring. The spring stiffness is really just a characteristic of the spring that defines behavior when it is stretched or compressed (I’m not sure if I am “allowed” to call it spring stiffness. In most cases it is called the spring constant) and the last variable is the damping constant, in general this applies a force counter to the direction of the velocity, causing it to reach the rest length of the spring sooner. You can think of the shock absorber of a car.

Now that we have all the information needed to simulate a soft body, let’s get to it!

I made 2 samples, a jelly blob and a rope. Both of them are the same in terms of maths, but the initial setup and the looping between the spheres and springs differ a little as the blob is 2 dimensional and the rope is linear. Now the first thing we need to do is set up the connection of springs and spheres. I will start with the blob.

As mentioned earlier, this shape is 2 dimensional (in terms of springs, not appearance) and we’ll need to nest 2 for loops to create the right amount. (In this example it is not really needed, but for clarity it looks better)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//first create the spheres. Amount = 50 in this case. for(int i = 0; i < Amount; ++i) { sphere[i] = new Sphere(); // I create random positions for the sphere sphere[i].position = vector3(random, random, random) sphere[i].force = 0; sphere[i].mass = 10; } for(int i = 0; i < Amount; ++i) { Spring* spring = new Spring(3.0f, 0.5f, 50.0f); // Spring(stiffness, dampingconstant, restlength) for(int j = 0; j < Amount; ++j { // I simply use a vector to store. Springs.push_back(spring); } } |

This will connect all the sphere that are randomly placed to each other with a spring. Now to move on to the simulation. What we aim to find out is the force between 2 spheres so we can find out the acceleration (F = ma remember?) and if we have that, we can just integrate that to get our results. Here’s how you do it:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
for(int i = 0; i < Amount; ++i) { for(int j = i + 1; j < Amount; ++j) { vector3 diff = sphere[j].position - sphere[i].position; float distance = diff.length(); diff.normalise(); float compression = distance - Springs[j].restlength; vector3 velocity = sphere[j].velocity - sphere[i].velocity; float force = (compression * Springs[j].stiffness) + velocity.dot(diff) * Spring[j].dampingconstant; sphere[i].force += diff * force; sphere[j].force -= diff * force; } } // We now have the force, we can calculate the velocity and position accordingly. for(int i = 0; i < Amount; ++i) { sphere[i].acceleration = sphere[i].force / sphere[i].mass; sphere[i].velocity += sphere[i].acceleration; sphere[i].position += sphere[i].velocity; } |

and actually this is all there is to it. You can apply this to whatever shape you want, granted you loop through it accordingly. Like for example a rope is 1 dimensional and only 1 loop is needed to check between the 2 spheres.

I am aware that there might be more elegant solutions to this problem, but the math stays the same regardless, you can play around with the mass and spring values to see what kind of effect it has. If everything is done right you should get the following effect.

And the rope could look something like this

anonOctober 28, 2011 at 00:56How about adapting it to work on GPU using the vertex shader to calculate a new position, given a buffer to read from (which is updated by the pixel shader)? Or was there a specific reason why this was done on CPU?

Joey FladderakOctober 28, 2011 at 01:16There is not really a specific reason why this is done on the cpu, but the main thing I am showing here is just the math behind it and I think that is more clearly shown in plain code. It shouldn’t be that hard to adapt this into a vertex shader like you said, but it wasn’t really my intention in the first place to do so 🙂