# Particle Systems

CS 481/681 2007 Lecture, Dr. Lawlor

## Mechanics

So.  Sir Issac Newton.  Specifically, mechanics:

F = m * A
A = dV/dt
V = dP/dt
• F- net force acting on a body.  Vector quanity, in the usual force units like Newtons (or cursed pounds).  Force is quite useful when bodies interact (like with collisions), because all interactions produce equal and opposite forces--if you push on me with force F, I push on you with force -F (negative F) (Newton's Third Law).
• m- mass of the body.  Usually just a constant.
• A- "acceleration" of a body.  Also a vector.  Units are bizarre--m/(s*s) ("meters per second squared").  For gravity, whose force is proportional to mass, the gravitation acceleration is then a constant, so if you only care about gravity, it's common to skip over force andjust start with the fixed gravitational acceleration.
• V- velocity of a body.  Yet another vector.  Units are m/s.
• P- position of the body, like you'd pass to glVertex3f.  Also a vector, measured in meters.
• t- simulation or animation time, in seconds.

## Physics for Particle Systems

The simplest place to apply Newton's laws is on "particles"--little dots or points floating in space.  Each particle has at least a position (P), but also optionally a color, a velocity (V), a size, etc.  The easiest way to draw particles is something silly like:
`	glBegin(GL_POINTS);	for (unsigned int i=0;i<particles.size();i++) {		glColor4fv(particles[i].color);		glVertex3fv(particles[i].P);	}	glEnd();`
So fill up the array of particles.  Draw them every frame.  This is easy, but they're just floating in space.

Now you just need to move the particles--just change their positions.  This is actually quite easy, and comes straight from the definitions above:
1. Pick a "timestep"--a small interval of time to simulate.  In the differentials above, this is "dt".
2. V = change in P / change in time.  Change in time==dt.  So solving for P, dt*V==change in P.  Thus we just run
P+=dt*V;
This alone will give you particles travelling in straight lines (Newton's First Law).
3. Compute the net force on the particle (see below).  Now F=m*A, so A=F/m  (or for gravity, A=constant).
4. A = change in V / change in time.  So solve for V, dt*A==change in V.  Thus we run
V+=dt*A;
This will allow the particles to respond to arbitrary forces, yet still have inertia.  It looks darn good, and it's easy!
Note: we are faking it here somewhat.  For a nonzero timestep, the velocity will change over the timestep, so our resulting P's are not exact, especially if V changes quickly.  If accuracy is important, you can use the second-order formula "P+=dt*V + 0.5*(dt*dt)*A;" or even something fancy like a Verlet integrator.

One place where virtually all normal integrations fail is for short-duration sharp impacts.  The problem is that the force changes extremely rapidly, changing the velocity in a jump.  Of course, we can fake this pretty easily for coordinate-aligned reflection planes:
`	if (P.x<0) {P.x=0; V.x=-elasticity*V.x;}`
Or for an arbitrary plane with outward-facing normal N and on-plane point O:
`	float dist=dot(P,N)-dot(O,N);	if (dist<0) {		P-=dist*N; /* move P to the plane surface */ 		V=elasticity*reflect(V,N);	}`
Elasticity controls how elastic the collision is--how much velocity is retained after the bounce.  elasticity==0.0 means particles do not bounce, like blobs of clay (or organs).  elasticity==1.0 means particles rebound with exactly as much velocity as they started with.

But usually computing the net force is easy--just sum up all the forces.  Forces can be as interesting as you like:
• Drag: force is directed backwards, with magnitude proportional to the square of the velocity, such as
F=-stickiness*area*V*length(V);     (so length(F) == length(V)2).
• Drift: force is directed in a random direction with fixed magnitude.
• Magnetic, electrostatic, gravitational, etc.   All you need is an equation.  And people are pretty forgiving about not-quite-right (or totally fictitious) forces, as long as their magnitudes are reasonable.
Once your particles fall offscreen (or just get wedged in one place for too long), it's a good idea to stop drawing them.  You can actually reincarnate the particle at some source (a "fountain" of particles), or

## Easy ways to Screw Up Physics

People spend their entire lives watching these laws in action, and they're amazingly good at detecting violations of them--and many games and animations "just don't look right" because the designers thought they could cut corners, or "just fake it".

For example:
• Motion.  Nothing looks weirder than an object's position spontaneously changing--like an object warping from place to place.  A *huge* number of character animation systems are "jumpy"; the character just warps from an animation in one orientation to another.
• Time.  Note that this really should be proportional to actual elapsed time, not something bogus like "frame count".  Frame times can vary dramatically between your machine and mine. *Most* games from the 386 era are unplayable today because the designers basically assumed the system could only draw a reasonable number of frames per second, like 30, but a Core2 gets 30,000 fps, so the animations and game logic run 1000x too fast!  Making simulation time a function of actual time is better than just limiting the framerate, because it lets a 100Hz-monitor guy actually see 100fps.
Luckily, the real laws of physics are strikingly easy to get right if you aren't trying to "make this cool thing happen" (that is, script the outcome of the motion).  If you just follow Newton's laws and let the coolness emerge, it's really easy.

## Cool ways to Render Points

• Use GL_LINES, and connect up the point's *old* location with its *new* location.  This gives nice motion blur to the points, and works well with a Verlet integrator.
• glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); and write to "gl_PointSize" in your GLSL code.  Then compute the point size (diameter, in pixels) based on the distance from the camera.  Voila--perspective-correct points!
`glEnable(GL_POINT_SPRITE_ARB);glTexEnvi(GL_POINT_SPRITE_ARB,GL_COORD_REPLACE_ARB,GL_TRUE);`