Assignment 1
Table of Contents
- 1 Preamble: Setting up Taichi
- 2 First part: Procedural turbulence for flow past an obstacle
- 3 Second part: Mass-spring cloth simulation
- 4 Submission
This assignment explores animations that can be computed using particle systems, and it has two parts. One is a 2D simulation of turbulent flow that uses procedural noise to concoct fake flow fields that look like fluids. The second is a 3D mass-and-spring cloth simulation, which simulates dynamic folding and wrinkling, but doesn’t handle self-collisions in the cloth.
1 Preamble: Setting up Taichi
Install Taichi with pip install taichi
and make sure you
are able to run the Julia Fractal.
We recommend using Anaconda to
install Python 3.8, and then using pip
to install Taichi
within a conda environment. This will help keep your setup for this
class separate from other Python environments you might need for other
projects. Once you have installed Anaconda you can do this with these
commands:
conda create --name cs5643 python=3.8
conda activate cs5643
pip install taichi pywavefront
Be sure your Taichi version is at least 1.4.
For the starter code, go to our assignment repository at this place and make a fork. Your fork should be a private repository. This will let you easily pull updates related to bug fixes and later assignments from our repository into yours.
Then clone your repository with:
git clone git@github.coecis.cornell.edu:yourNetID/<path to your repo>.git
2 First part: Procedural turbulence for flow past an obstacle
For the first part of this assignment you will implement an animation of particles in a 2D flow field that looks like the flow of a fluid around a circular obstacle. We’ll use the approach known as curl noise, as introduced in Bridson 2007.
The idea of curl noise is to generate divergence-free velocity fields by defining a vector field known as the “potential” and then defining the the velocity field as a derivative of the potential—in particular, the curl of the potential. Because the divergence of the curl of any vector field is zero, this ensures your velocity is divergence-free so that particles will flow around without clumping.
This assignment is in 2D, so the potential is considered to be a
vector pointing in the
In this assignment we will define the potential as the combination of three pieces:
- A “background flow” potential
that ramps up linearly from bottom to top to define a steady flow from right to left. - A modulation function
that passes smoothly through zero at the surface of the obstacle. - A procedural turbulence function
- A turbulence window function
that is nonzero in the region to the left of the obstacle where we want to see a turbulent wake, and ramps down smoothly to zero outside that region.
The final potential is
You may use the starter_code.py
to kick-start your
implementation of this part.
2.1 Laminar flow
Start by getting the laminar part of the flow (i.e. without the
noise-based “turbulence”) working. Start with a simple first-order
particle system (you can borrow from the lecture demos if you like) that
moves particles according to a velocity field, and test it out with some
simple fields like a rotation (swirl_particles.py
) or a
constant steady flow
It would be nice to introduce particles flowing into the right edge at a steady rate. You can do this by initializing all the particles at the right edge with random vertical coordinates, then steadily ramping up the number of particles that are active, so that at each frame you get some more of your pre-initialized random particles coming into the simulation. When they get to the left edge, re-initialize them at the right edge; this way they will show up as if they were yet more newly generated particles.
The phase 1 video shows the result of this with a velocity of 1 and 60 particles generated per second (1 per frame).
Then define a potential that will produce the same velocity field when you take its curl. In our 2D setting that means concocting a function whose gradient points straight up with magnitude equal to the speed you want.
In the homework we computed the curl of a potential analytically.
This is quite doable for the types of potentials we are using, but the
need to maintain code for the derivative and update it whenever you
change the potential just makes it harder to play around with the
system. Since high accuracy in the velocity is not required, we
recommend for this programming assignment that you compute the
derivative of the potential using finite differences, using a centered
difference with a stepsize about the square root of the machine
precision. For 32-bit floats the machine precision is around
To get a flow that avoids the obstacle, multiply the potential by
Bridson’s smooth ramp function, which is a function of distance from the
“surface” of the circle. To get the same results as us put the obstacle
at (0.7, 0.5) with radius circle
function in the Taichi GUI class).
After including this factor, you should get a flow field that does
not make the particles enter the obstacle, but rather steers them around
it. They might not go around in the direction you would expect, since
whether they wind up going clockwise or counterclockwise depends on the
sign of the background potential
2.1.1 Field display for debugging
It’s nice to see the field you are designing rather than having to wait for the particles to advect through it. You can use the line integral convolution code from the lecture demo to draw the field in the background, which may be helpful in setting up the next part. If you copy that in and run it on the laminar flow field you should get something like this.
2.2 Single band curl noise
Now to the turbulence part! Import the 2D Perlin noise
When using procedural noise we describe a noise as being at a
particular scale or frequency; for the purposes of this assignment we
will define “noise with scale
Here is curl noise at scale 0.1 visualized with LIC.
2.3 Multi-band turbulence
Turbulence in real fluids is a multi-scale phenomenon, with swirling
eddies at a whole spectrum of scales. In particular, the famous Kolmogorov
spectrum tells us to expect kinetic energy per unit mass at scale
In our simulation of turbulent fluids, the velocity of one band of
turbulent flow / curl noise is a scalar amplitude
Eventually, we would like to add together a number of noise functions
at different scales and velocities. We will have a number of bands
indexed by
2.4 Putting it all together
Now we have almost all the pieces to build our flow with a turbulent wake behind the obstacle. We only need a smooth window function that we can multiply the turbulence by so that it only affects the desired area behind the obstacle. For this Bridson used a product of three smoothstep functions that ramp down the turbulence at the top, bottom, and right sides of the wake.
You can compute this window in two steps:
First, get the product of two smoothstep functions whose nonzero region is visualized with grayscale values in the
figureSecond, get the smoothstep function along x direction as shown in the
figure.
The product of
Finally assemble the four pieces (background, object influence, turbulence, and window) as described above to produce the complete field. If you advect particles through this field you should get something like this.
2.5 Time varying flow
This flow looks nice for a while but it is boring and unrealistic how
it stays steady over time; real turbulence is always evolving. It is
simple to fix this: promote the noise from 2D to 3D and pass the time as
the third coordinate! It is good to scale the time in the same way as
the
With just that one change you can get more interesting motion (computed using time scale
2.6 Optional extras
For a modest amount of extra credit, do something cool with this system that we didn’t specify! Some ideas:
- Make a flow around multiple obstacles of different shapes.
- Make a flow for steam billowing out of a smokestack.
- Make an animation of the smoke that rises from an incense stick or snuffed-out candle, which flows in a smooth column for a while and then becomes turbulent, slows, and spreads horizontally.
3 Second part: Mass-spring cloth simulation
For the second part, you will implement a simple cloth simulator
where a square piece of cloth collides with an object. The starter code
mass_spring_cloth.py
provides mesh constrution and
rendering for the scene.
If you have not done so for the first part, you will need to run
pip install pywavefront
to install the package needed for
reading the spherical collision object.
3.1 Cloth initialization
A piece of square mass-spring cloth is composed of
Mass
. Here we set to be constant and assume that all particles have the same mass of .Position
Velocity
The position and velocity are affected by different forces which we
will implement in subsequent steps, and follow Newton’s second law:
You will use Symplectic Euler to update the positions of the particles.
The first task is simple: Initialize the cloth by setting the
particle positions in the Taichi kernel init_cloth()
. In
the starter code we have defined a Taichi field of x
that holds the positions of the
The particle positions from x
are read by
update_vertices()
, where the particle positions are simply
copied to the mesh vertex positions.
The result should look like this
where
To make sure you are indeed getting a full quad, move the camera by dragging the left mouse button in the GUI.
3.2 Springs
There are three types of springs that connect the particles. For an
arbitrary particle at
Structural: (At most) four neighboring particles at
Shear: (At most) four neighboring particles along the diagonal at
Flexion: (At most) four particles that are two particles away at
The illustration above demonstrates how the different kind of springs are attached to the particles of a 2D grid.
Each type of string has a different stiffness, denoted by
We assume that the cloth is initially at rest, so you can calculate the rest lengths of a spring by the distance between the particles connected by the spring in the initial state.
3.3 Forces
3.3.1 Spring forces
For two particles at positions
3.3.2 Gravity
The gravity acting on each particle is simply
Additionally, you will write the time stepping function following
Newton’s second law. You will need to create a vector field for storing
the velocities of particles, in addition to storing the positions in
part 1. Pin the positions of the particles at indices
After implementing spring forces and gravity as well as setting up
the time stepping function, your result should look like this. The demo has
3.3.3 Damping
Now we will add some damping so that the simulation converges. There are two types of damping:
- Spring damping: For two particles at positions
connected by a spring of stiffness , the damping force corresponding to the spring force acting on particle at position is
- Viscous damping:
Again, make GUI sliders for the two damping parameters
3.4 Collision with a sphere
Now we will bring in a sphere and have it collide with the cloth.
During the integration, for each particle, check if the distance from
the center of the sphere is smaller than the sphere radius. If the
normal component of the velocity is into the surface, project the
velocity to the tangent plane of the sphere at the contact point; if the
normal component of the velocity is away from the surface let it be,
i.e.
3.5 Extra features (optional)
- Explore with different types of collision objects, e.g. shapes other than the sphere, or even arbitrary meshes.
- Explore the parameter space of the mass spring system, and set up the parameters to replicate the behavior of a certain material such as silk or cardboard.
4 Submission
You need to include two things in your submission:
- A pdf file including a link to the chosen commit for your submission. If you submit a direct link to your repository, we would assume that you would like us to look at your latest commit.
- A demo video demonstrating the functionalities of your simulators (please make sure that we can easily run your code and generate similar results shown in the demo).