Particle Systems
Great Moments in
Related Computer Graphics History
”Particle animation and rendering using data parallel
computation”, Karl Sims (available via NYU network/proxy)
“Particle Systems, a Technique for Modeling a Class of Fuzzy
Objects”, Reeves (available via NYU network/proxy)
How my Dog learned Polymorphism
Particle Systems (Siggraph) The term “particle system” was
coined in 1983 by William T.
Particle System API, by David K. McAllister Reeves as he worked to create
the “Genesis” effect at the
Particle Systems by Allen Martin end of the movie, Star Trek II:
The Wrath of Khan.
Physically Based Modeling, Particle System Dynamics by
Andrew Witkin “A particle system is a
collection of many many
A particle system is a collection of independent objects, minute particles that together
often represented by a simple shape or dot. It can be used to represent a fuzzy object. Over
model many irregular types of natural phenomena, such as a period of time, particles are
explosions, fire, smoke, sparks, waterfalls, clouds, fog, generated into a system, move
petals, grass, bubbles, and so on. In a system, each particle and change from within the
will have its own set of properties related to its behavior system, and die from the
(for example, velocity, acceleration, etc.) as well as its look system.”
(for example, color, shape, image, etc.). – Reeves Particle Systems: a
Technique for Modeling a
One of my favorite examples of a particle system in action Class of Fuzzy Objects.
is Karl Sims’ “Particle Dreams”, a short video visualizing
different complex phenomena (snowstorms, waterfalls, and
a “Self-Breathing Head,”) by applying behavior rules to thousands of tiny particles. (Note this
was made in 1988!).
We are going to look at implementation strategies for coding a particle system. How do
we organize our code? Where do we store information related to invididual particles vs.
information related to the system as a whole? The examples we’ll look at focus on
managing the data associated with a particle system. The examples will use simple shapes
for the particles and apply only the most basic behaviors (gravity, etc.). However, by
using this framework and building in more interesting ways to render the particles and
compute behaviors, you can achieve lots of different effects.
The particle object. We first need to declare our class and decide what instance variables we
want to have:
class Particle {
PVector loc;
PVector vel;
PVector acc;
float r;
float timer;
This is nothing really new, just some variables to keep track of location, velocity, acceleration,
and size. However, we are adding an important element, a timer to keep track of the particle’s
life. Our particles, sadly, will not live forever.
//function to update location
void update() {
[Link](acc) ;
[Link](vel) ;
timer -= 1.0;
}
In the above method, the timer counts down each cycle. We would probably want to improve this
and keep track of how fast the timer should count in a separate variable, but this will do for now.
We also want to include a method that tells us if the object is alive or dead based on the state of
the timer. That way the system can know whether it needs to keep it around.
boolean dead() {
if (timer < = 0.0) {
return true;
} else {
return false;
}
}
You can imagine easily what else would be included here: constructors, a “render” method, etc.
And in fact, we could stop here and just make an array of these particles, call methods on them in
a for loop and be done with it. However, we want to go a step further and introduce not only a
“Particle” class, but a “ParticleSystem” class, i.e. a class that manages the collection of particles
itself. Our main program, therefore, doesn’t necessarily require any references to individual
particles specifically. It references the systems themselves, which in turn manage the particles.
To do this efficiently, we want to take a look at how we can have store a variable collection of
objects.
Resizable arrays
Most of our examples up until now (with the exception of a few) have used standard java arrays
to keep track of ordered lists of information. We might have an array of 10 objects, looping
through them each cycle to update locations, render them, etc. However, in the case of a standard
array we are limited to having 10 and only 10 objects. There are certainly alternatives (using a
very large array and having a separate variable to keep track of how much of the array we should
use at any given time), but it would be much more useful if we could dynamically size the array
at run-time. In the case of a particle system, this will really help.
To accomplish our goal of having a resizeable array, we will use the java class ArrayList, which
can be found in [Link]. This class is not part of the Processing reference and in order to know
how to use an ArrayList instance, we must consult the java API.
Using an ArrayList is conceptually similar to a standard array, but the syntax will be quite
different. Here is some code (that assumes the existance of a class “Particle”) demonstrating the
same functionality, first with an array, and second with an ArrayList. (Note we would never
actually write the code below, it’s just meant to illustrate the differences between arrays and
ArrayLists).
int MAX = 10;
// Declaring the array
Particle[] parray = new Particle[MAX];
// Declaring the arraylist
ArrayList plist = new ArrayList() ;
// The following code you would usually find in setup
for (int i = 0; i < [Link]; i++) {
parray[i] = new Particle() ;
}
for (int i = 0; i < MAX; i++) {
[Link](new Particle()) ;
}
// The following code you would usually find in draw
for (int i = 0; i < [Link]; i++) {
Particle p = parray[i];
[Link]() ;
[Link]() ;
}
for (int i = 0; i < [Link]() ; i++) {
Particle p = (Particle) [Link](i) ;
[Link]() ;
[Link]() ;
}
Note that in this last for loop, we have to make sure to cast the object we pull out of the
ArrayList. The ArrayList doesn't keep track of the type for things stored inside -- it's our job to
remind it!
A Particle System Class
The main piece of the particle system class will be the ArrayList as it will contain all the
particles in the system. However, some other "global" variables for a particle system might be
required, such as an origin point for where particles are birthed, an image reference for a
particle texture, etc.
class ParticleSystem {
ArrayList particles; //an arraylist for all the particles
PVector origin; //an origin point for where particles are birthed
ParticleSystem(int num, PVector v) {
particles = new ArrayList() ; //initialize the arraylist
origin = [Link]() ; //store the origin point
//add "num" amount of particles to the arraylist
for (int i = 0; i < num; i++) {
[Link](new Particle(origin)) ;
}
}
Clearly, the next step is to write a method that calls methods on all the particles in the system. As
we've seen from how an ArrayList works, this is fairly simple:
void run() {
for (int i = 0; i < [Link]() ; i++) {
Particle p = (Particle) [Link](i) ;
[Link]() ;
}
}
However, while we cycle through each particle, we want to check and make sure the particle is
still alive; if it is not, we should remove it from the ArrayList. There is a problem here -- when an
element is removed at a specified position in this list, any subsequent elements are shifted to the
left (i.e. one is subtracted from their indices). This will result in skipping elements as they are
deleted (if item N is deleted, item N+1 becomes item N and is not checked since the loop has
already checked item N!) This is easily, solved, however, by going through the ArrayList
backwards.
void run() {
// Cycle through the ArrayList backwards b/c we are deleting
for (int i = [Link]()-1; i >= 0; i--) {
Particle p = (Particle) [Link](i) ;
[Link]() ;
if ([Link]()) {
[Link](i) ;
}
}
}
Finally, we can implement additional functionality to our system, such as methods that will birth
new particles and a method that will test if the entire system itself is dead:
void addParticle() {
[Link](new Particle(origin));
}
void addParticle(Particle p) {
[Link](p);
}
boolean dead() {
if ([Link]()) {
return true;
} else {
return false;
}
}
Once we have finished implementing the particle class and the particle collection class, our main
program code is nice and elegant. We only have to declare a ParticleSystem as a global variable,
call the constructor in setup() to instantiate it, and then call the run function in draw() (as well as
choose to call "addParticle()" whenever new particles should be created.)
ParticleSystem ps;
void setup() {
size(200,200);
ps = new ParticleSystem(1,new PVector(width/2,height/2,0)) ;
smooth() ;
}
void draw() {
background(255);
[Link]();
[Link]();
}
OOP -- Inheritance
In the case of a particle system, we will often want to have systems containing different types of
particles. In order to accomplish this, we would like to avoid writing a new class for every single
particle. A better solution would be to create "subclasses" of our master particle class that could
use the exiting data and functionality of a regular ol' particle, adding other features as necessary.
Object oriented programming allows us define classes in terms of other classes. In other words, a
class can be a subclass (aka "child") of a super class (aka "parent"). This concept is known as
"inheritance."
Take this very typical example, where we have a class containing a few instance variables, a
constructor to fill them, and a method that increments the x and y variables randomly.
class Shape {
float x;
float y;
float r;
Shape(float x_, float y_, float r_) {
x = x_;
y = y_;
r = r_;
}
void jiggle() {
x += random(-1,1) ;
y += random(-1,1) ;
}
}
Now what if we create a subclass from Shape (let's call it "Square"). It will inherit all the
instance variables and methods from shape. We write a new constructor with the name "Square",
however, here we are executing the code from the parent class by calling "super".
class Square extends Shape {
//inherits all instance variables from parent
//we could add variables for only Square here if we so
Square(float x_, float y_, float r_) {
super(x_,y_,r_) ;
}
//inherits jiggle method from parent
//adds a new render method
void render() {
rectMode(CENTER) ;
fill(255) ;
noStroke() ;
rect(x,y,r,r) ;
}
}
Here is another subclass with some additional functionality. It adds an instance variable to keep
track of color (this is just to show how this is possible, most likely we would want the super class
to include color). It also calls the parent jiggle method (with super), but adds some additional
code.
class Circle extends Shape {
//inherits all instance variables from parent + adding one
color c;
Circle(float x_, float y_, float r_, color c_) {
super(x_,y_,r_) ; // call the parent constructor
c = c_; // also deal with this new instance variable
}
//call the parent jiggle, but do some more stuff too
void jiggle() {
[Link]() ;
r += random(-1,1) ;
r = constrain(r,0,100) ;
}
//adds a new render method
void render() {
ellipseMode(CENTER) ;
fill(c) ;
noStroke() ;
ellipse(x,y,r,r) ;
}
}
Polymorphism
Polymorphism (i.e. many forms) refers to the concept that we can treat an object instance in
multiple ways. A Circle is a Circle, but it is also a Shape so we can refer to it as either.
Shape c1 = new Circle(100,100,20,color(255)) ;
Circle c2 = new Circle(100,100,20,color(255)) ;
Both of the above lines of code are legal (!!). Even though we declare c1 as a Shape, we're really
making a Circle object and storing it in the c1 reference. (We can safely call all the Shape
methods on c1 b/c the rules of inheritance dictate that a Circle can do anything a Shape can). At
run-time, however, java will determine that this object really truly is a Circle and run the proper
methods. Amazing! This becomes particularly useful when we have an array. Here we can make
an array of "Shapes", put both Circles and Squares into the array, but not have to worry about
which are which -- that will all be taken care of for us!!
Shape[] s = new Shape[25];
for (int i = 0; i < [Link]; i++) {
int r = int(random(2)) ;
//randomly put either circles or squares in our array
if (r == 0) {
s[i] = new Circle(100,100,10,color(255,0,0)) ;
} else {
s[i] = new Square(100,100,10) ;
}
}
Later, we can run through the array like so. Again, even though some of the elements are circles
and some are squares, we don't have to specify in our code since we can treat them all in the
general form as "shapes".
for (int i = 0; i < [Link]; i++) {
Shape ashape = s[i];
[Link]() ;
[Link]() ;
}
For a much better explanation of polymorphism, check out How my Dog learned Polymorphism.