# Boundary Conditions for Particle Systems

CS 493/693 Lecture, Dr. Lawlor

Making particles bounce off a boundary is a surprisingly subtle problem.  The inherent difficulty is that boundaries are discontinuities, where a particle falling at 1g in gravity suddenly smacks into the floor, which can easily push back with hundreds or thousands of gravities of accelleration.  Because our timestep is designed to work at 1g, not 100g, and certainly not for timesteps with half of one and then half of the other, it's pretty difficult to track the actual forces involved in the collision process with any reasonable efficiency.

But it's easy to fake something close that usually works by just:
1. Detect that you've crossed into bad territory.
2. Flip the velocity so you'll tend to leave the bad area.
A typical implementation looks like:
`	if (particle.pos.z<0.0) { /* we're underground! */		particle.vel.z=-0.7*particle.vel.z; /* semi-elastic bounce */	}`
The constant 0.7 is the elasticity of the collision:
• 0.0 means the particles land on the floor, splat, and stick.  Like toast, or wet clay.
• 0.3-0.7 or so gives a reasonable bounce, eventually decaying to zero.  Most objects fall in this range.
• 1.0 means the particles rebound to their exact starting height, like perfect ball bearings.
• 2.0 means the particles rebound higher with each bounce, like flubber.
There are several things that go wrong with this, however--the worst is that particles often get "stuck" on the boundary, since flipping the velocity only tends to make them leave if the initial velocity was high enough (and pointing in the direction you expected).  If the particle was already leaving, flipping the velocity just makes the problem worse in the next timestep.

So a reasonable modification is to push the particle out of the ground, in addition to modifying the velocity:
`	if (particle.pos.z<0.0) { /* we're underground! */		particle.vel.z=-0.7*particle.vel.z; /* semi-elastic bounce */		particle.pos.z=0.0; /* push particle out of the ground */	}`
This works, but note that we're adding energy to the system by doing this.  With a large enough timestep, this can cause particles to bounce out of control, but with a reasonable timestep, the overall motion is pretty good.  If you're concerned about this energy addition, the right answer is to solve for the sub-timestep moment when particle.pos.z=0, take a sub-timestep in velocity to that instant, flip the velocity, and then finish out the timestep.

However, there is still a "jitter" problem with particles that should be sitting stationary on the floor:
• In even timesteps, gravity pulls the particle down, and the particle falls below the floor.  The code above pushes it back up to z=0, which is fine.  But the velocity is flipped around to be small and positive.
• In odd timesteps, the positive velocity actually pulls the particle a tiny distance up off the floor.
This can matter if you're taking large timesteps, or keeping a close eye on things on the floor.  The fix is to penalize the positive velocity we give to the particle, something like:
`		particle.vel.z=-0.7*particle.vel.z-0.2;`
Precisely, we want to cancel out the velocity from a few steps' accelleration due to gravity, so the 0.2 above should be 2*dt*gravity_accelleration.  This means particles sitting on the floor will always have a small negative velocity, but that's OK because we reset the position every step.

More generally problems at the boundary conditions, like most discontinuities, can almost always be resolved by one of the following:
• Shrinking the timestep (dt) until the discontinuity doesn't matter.  This uses up CPU time.
• Using some elegant mathematics to determine the exact answer.  This can take serious thought.
• Slapping in a fudge factor to kill off the bad behavior.  This often only works for a particular range of parameters, and can be tricky to create.

## Curved Boundaries

Curved boundaries are reasonably easy to model--the "inside boundary" check becomes a distance-from-surface computation (for a sphere, a radius computation), and the velocity bounce is implemented as a reflection about the surface normal.  The GLSL "reflect" function is quite handy for this--if your vector library doesn't have "reflect" already, definitely add it!
`vec3 reflect(vec3 I,vec3 N)   {return I-2.0*dot(N,I)*N;}...	particle.vel=0.7*reflect(particle.vel,normalize(surface_normal));`

## Multiple Boundaries

It can become exceedingly difficult to push particles out of a sharp corner between two boundaries.  Often pushing the particle outside boundary A just pushes it deeper into boundary B, and vice versa.  Many commercial computer games allow the player to become irrevokably wedged into boulders or other randomly oriented geometry, so the problem isn't easy to solve in general.

One simple fix is to resolve the "fix A, or B?" boundary question by adding a third boundary C, to push the particle away from problematic corners.  Often simulated worlds have many invisible collision-only geometry "blockers", if only to keep the viewers corralled inside the good looking area of the world.