Simulate fuzzy phenomena with particle systems

Use particle systems to simulate explosion rings, fireworks explosions, vapor trails, and more

Computer graphics (CG) is important to the games we play, the movies we watch, the design of our vehicles, and more. CG uses traditional polygon-based techniques to model and render classical geometry (cones, spheres, cubes, etc.). In contrast, the geometry of natural phenomena—such as fire and flowing water—needs a different modeling, rendering, and animation technique: particle systems.

After introducing you to the particle system concept and presenting particle systems in a historical context, this Java Fun and Games installment takes you on a tour of Java-based software that I created to build and play with particle systems. The article then reveals a demo applet that uses this software to simulate explosion rings, fireworks explosions, and vapor trails.

Particle system basics

A particle system is a CG technique that combines modeling, rendering, and animation to simulate fuzzy phenomena (also known as fuzzy objects); examples are various kinds of explosions, flowing water, clouds, fire, meteor trails, snow, sparks, fog, and falling leaves. These phenomena are called fuzzy because, due to the absence of straight edges, they appear blurred when rendered: they are composed of particles.

Particle systems manipulate collections of particles. Each particle has various attributes that affect the particle's behavior, along with where and how (as a point composed of one or more pixels, a line, an image, and so on) the particle is rendered. Common attributes include position, velocity (speed and direction), color, lifetime, age, shape, size, and transparency. Most of these attributes vary their values during the particle's existence:

  • Birth: Particles are generated somewhere in the fuzzy object's vicinity. Each of the particle's attributes is given an initial value. This value may be fixed or determined by some stochastic process.
  • Life: Various particle attributes (like position and age) tend to vary over time. For an explosion, each particle's color may darken as the particle moves further away from the explosion's center. This signifies an energy decrease (the particle is dying).
  • Death: Particles typically have two attributes that determine the length of the particle's life: age and lifetime. Age determines the amount of time the particle has already lived. It typically is initialized to 0 and is measured in animation frames (iterations of an animation loop). Lifetime is the maximum amount of time the particle can live and also is measured in frames. The particle is destroyed when its age reaches its lifetime. Various factors may result in a premature death, including a particle moving out of the viewing area and not returning to that area, and a particle's color fading to black prior to its age reaching its lifetime. In this case, we could assume that a black particle indicates zero energy (think explosion particles).

Particle systems have been in use for several decades. Perhaps their earliest use dates back to 1961-1962. According to "Welcome to PONG-Story," in 1961, three MIT students created a video game called Spacewar for the PDP-1 minicomputer (released by Digital Equipment Corporation in November 1960). This game featured a particle system to simulate explosions.

Spacewar involves a pair of spaceships battling around a star whose gravity pulls them in. While trying to avoid the star, these spaceships fire torpedoes at each other. As Figure 1 reveals, the particle system rips apart a spaceship when the spaceship is hit by a torpedo.

Figure 1. The lower-right spaceship's breakup is generated by a particle system

The Spacewar particle system also generates explosions whenever both spaceships collide. Despite a low resolution and the absence of color, Figure 2 indicates a degree of realism that the particle system achieves when simulating explosions.

Figure 2. The particle system manages two explosions when both spaceships collide

If you would like to play the original Spacewar game (which is in the public domain), point your Web browser to the Spacewar instructions page and follow the instructions. After you download the Perl and Java source files, you will need to install a copy of Perl on your system (if you don't already have Perl installed) and execute the following commands to create spacewar.bin:

  • perl spacewar.mac >out expands macros in spacewar.mac's PDP-1 assembly language and stores the expanded assembly language in a file named out
  • perl out >out1 performs a two-pass assembly of out's expanded assembly language and stores a binary listing in a file named out1
  • perl out1 extracts the object code from out1's binary listing and stores it in spacewar.bin

After you create spacewar.bin, compile, which also compiles all of the associated Java source files. Execute java pdp1b and click the resulting GUI's Run button to play. The pdp1b program loads spacewar.bin and starts a PDP-1 emulator to interpret this file's contents.

Subsequent to Spacewar, particle systems were used in other games (such as Asteroids). But their potential was not realized until 1983. In that year, Lucasfilm's William T. Reeves published his paper "Particle Systems: A Technique for Modeling a Class of Fuzzy Objects." This paper formalized the particle system concept. It was well received because it described the particle system that generated the Genesis Effect's planetary wall of fire in the movie Star Trek II: The Wrath of Kahn. If you have an account with the Association for Computing Machinery, you can read Reeves's paper, a link to which appears in Resources.

Particle system software

Particle system software lets you simulate fuzzy phenomena by supporting the modeling, rendering, and animation of particles. My Java-based particle system software provides the PS class to support modeling and facilitate animation. It also provides the Display class to support rendering. I first discuss Display because PS depends on this class.

The renderer

The Display class is a java.awt.Canvas component that renders particles. It renders particles as individual pixels, although there is no reason why Display could not render them as groups of pixels, images, and so on.

When you create a Display object, you identify a window and a viewport. CG courses typically define window as the portion of a 2D world that you want to see. The window's coordinates are specified as floating-point values and are known as world coordinates. CG courses also typically define viewport as the portion of the screen where you want to display the window's contents. The viewport's coordinates are specified as integer values and are known as screen coordinates. A transformation known as the windowing transformation maps world coordinates to screen coordinates. Figure 3 illustrates this mapping in terms of world point (xw, yw) and equivalent screen point (xs, ys).

Figure 3. The windowing transformation. Click on thumbnail to view full-sized image.

The window's edges are specified at x = wl, x = wr, y = wb, and y = wt. The corresponding viewport edges are located at x = vl, x = vr, y = vb, and y = vt. The following equations use this information to transform world point (xw, yw) to screen point (xs, ys):

xs = (vr - vl) / (wr - wl) * (xw - wl) + vl

ys = (vt - vb) / (wt - wb) * (yw - wb) + vb

These equations determine the position of point (xw, yw) within the window as a fraction of full displacement from the window's lower-left corner, and interpret this fraction as a fraction of full displacement across the viewport. The addition of the viewport's lower-left corner provides the (xs, ys) position on the screen.

Create a Display object by invoking the Display(double wl, double wb, double wr, double wt, int width, int height) constructor. The wl and wb values identify the lower-left corner of a window, whereas the wr and wt values identify the window's upper-right corner. These values are specified in world coordinates. The width and height values identify the extents of a viewport associated with the window. All of these values should be chosen so that the window and viewport have the same shape. Otherwise, as Figure 3 reveals, you end up with distortion. Because I want the viewport to occupy the entire component's drawing surface, there is no point in specifying the upper-left corner, which is (0, 0).

The following code fragment creates a window with a lower-left corner at (-100.0, -100.0) and upper-left corner at (100.0, 100.0). The extents are 50 pixels less than the width and height of the applet, to prevent distortion:

 display = new Display (-100.0, -100.0, 100.0, 100.0, getWidth ()-50, getHeight ()-50);

After creating the Display object, your animation logic can call any of the following Display methods:

  • void clear() clears the display to black. Call this method at the beginning of each animation frame to erase previously drawn particles. You normally do this before updating the particle system.
  • void drawLine(double x1, double y1, double x2, double y2, Color c) draws a line on the display from (x1, y1) to (x2, y2) and in the color specified by c. Values passed to x1, y1, x2, and y2 are specified in world coordinates. Call this method to draw any shapes (like a rocket ship) after updating the particle system for the current animation frame.
  • void plot(double x, double y, Color c) draws a pixel on the display at (x, y) and in the color specified by c. Values passed to x and y are specified in world coordinates. Although the PS class calls this method to plot particles, you can also call this method to assist in drawing any shapes after updating the particle system for the current animation frame.

Because the aforementioned methods produce output in a buffer (to eliminate one source of flicker), you need to call repaint() on the Display object to make the display's buffer visible.

You're probably wondering why I went to the trouble of creating a window and a viewport. In addition to extra code not necessary if integers were used instead of floating-point values, these floating-pointing values yield simulations that are slower than their integer-based counterparts. I had two reasons for doing what I did:

  • Accuracy: When you simulate with integer values, you'll lose some information due to truncation. My original integer-based vapor trail simulation resulted in unsightly empty streaks appearing in the trail. This was caused by the discrete nature of integers as opposed to the continuous nature of floating-point values.
  • Simplify transition to 3D: At some point, I'll want to transition this particle system software to 3D. A 3D graphics package often provides the capability to plot points in world coordinates using floating-point values.

The engine

The PS class is the engine of my particle system software. It models and generates particles, updates the particle system during an animation loop, and kills off those particles that die naturally or prematurely. After creating a Display object, your application or applet creates a PS object by invoking the PS(Display display) constructor to initialize the engine. Initialization saves the Display object for later access by the particle system update logic, creates four color tables describing shades of green, red, white, and yellow, and creates an array of particle objects that each describe an individual particle.

After creating the PS object, your animation logic can call the following PS methods:

1 2 3 Page 1
Page 1 of 3