Simulating Smoke

Antoine McNamara and Adrien Treuille
University of Washington
CSE 557 - Final Project
3/21/2002


smoke gun
(high-res Quicktime ~3.2MB) (low-res Quicktime ~0.9MB )

random smoke
( high-res Quicktime ~38MB) (low-res Quicktime ~3.2MB)



Introduction:

For our final project, we developed a simulator which models, animates and renders smoke according to the laws of fluid dynamics. Animating smoke has long been a topic of interest in computer graphics, but only in the past 5 years have researchers used physical simulations to generate these animations. Smoke, like other fluids, is extremely difficult to animate by hand, and there is a clear desire for simulations that produce realistic results, preferably that allow a certain degree of control and that are not prohibitively expensive, computationally.

We can consider slow-moving gases to be incompressible fluids, which are modeled by the Navier-Stokes equations. Foster and Metaxes first used the Navier-Stokes to animate gases and produced nice results on relatively coarse grids [2]. In [4], Stam proposed a method using similar fields, but with an implicit, semi-Lagrangian technique for updating the grids which resulted in much simpler computations that were guaranteed to be stable. In 2001, Fedkiw, Stam, and Jensen extended this algorithm specifically for smoke simulation, noting that the viscosity term in the Navier-Stokes equations didn't contribute dramatically to the motion of gases, and proposing a technique known as "vorticity confinement" to inject velocities back into the system that might have been lost in the numerical dissipation of the implicit semi-Lagrangian integration scheme.



Implementation:

Our smoke simulator is divided into two parts: a physical simulator and a renderer. During each iteration we evolve the physical simulation by a small time step, then pass that information to the renderer. We have created simulations and renderings both in 2D and in 3D.

i. Physical Simulation:

The central component of the physical simulator is a grid of velocities u, representing currents of air. As in [1], we evolve the velocity grid over time according to the Euler equation for fluid dynamics:

Euler Equation

Here rho is the density, p is the pressure and f is an arbitrary force. (These equations are a simplification of the Navier-Stokes equations that ignore viscosity.) We also wish that the air currents preserve the mass of any smoke that passes through them. Technically speaking, this means that the air velocity field be "divergence free." By defining a linear operator P that projects the velocity field onto a divergence-free velocity field, we can derive the following equation:

Projection Equation

The details of this derivation can be found in [4]. This equation forms the basis for our simulation. The algorithm is as follows:

for each iteration
    add the force                                              // f
    advect the velocity along itself                           // -(u del)u
    project the velocity field onto a divergence-free field    // P(...)

In order to advect the velocity we use a second-order adaptive Runge-Kutta path tracer. To compute the projection step we must solve a Poisson differential equation. (This is a consequence of the mathematics. We refer the reader to [4] for details.) We solve the Poisson equation by spacially discretizing it, yielding a sparse linear system of equations. We then use the Conjugate-Gradient method to approximate a solution.

The above algorithm gives us a field u of air currents that evolves over time according to the laws of fluid dynamics. We then inject two "substances" into the air: smoke and temperature. We model these substances as a grid of scalar values. In the case of smoke, each scalar value represents the density of smoke at a particular grid point. For each iteration of our algorithm we advect (transport) these scalar values through the grid along the air velocity field.

Finally, there is a feedback mechanism: as the temperature and smoke densities get transported through the air, they affect the air currents. Hot air rises, while dense smoke falls.

ii. Rendering:

To render the 2D animations, we simply draw the density grid on the image plane, using OpenGL's GL_QUADs for rendering at near interactive rates (for grids up to about 200x200). The produces very nice results, but, of course, lack shading and self-shadowing. In 3D, we use much more sophisticated rendering techniques.

We implemented a stripped-down ray tracer (with no reflection or refraction rays), which casts rays through the grid and attenuates the intensity of the ray by the amount of smoke it passes through. As a preprocessing step, we shoot a ray from the center of each voxel toward the light source to determine the color of the smoke at that particular grid location (we only use a single light source, but in practice we could do the same for multiple lights and sum the resulting intensities). We determine the length of the path through each voxel and take the ratio over the maximum path length as the "amount" of smoke the ray passes through. At each voxel, the light intensity from the rest of the path is scaled by this ratio, resulting in an exponential decrease as we proceed through the smoke.

Once this preprocessing is complete, we cast rays from each pixel in the viewing direction (we use a parallel projection in all of our examples, but it trivially extends to perspective projections). Each ray marches through the voxels in the same fashion as before, only now it uses the path length through each voxel to decide between the preprocessed smoke color and the color along the rest of the ray (which terminates in a background color).

When the viewing rays exit the cube on which the grid is defined, we use one last step to cast shadows. A ray is cast from the exit point onto the ground plane, followed by another ray from this intersection point in the light direction. If this ray passes through any smoke, the background color should be attenuated to cast a shadow. We could easily repeat the method used in the preprocessing step to accurately determine the attenuation, but in the interest of reduced computation, we simply test the ray for intersection with the box and then use the precomputed value at the nearest voxel. This essentially projects the grid onto the floor leading to aliasing of the shadow, but very little computation is required for the shadow and the resulting shadow still serves the purpose of grounding the smoke.




Discussion:

This project was really rewarding and lots of fun. We essentially accomplished what we sought, but there were a few snags along the way.

The aspect of the project that gave us the most trouble was projecting the velocities down to a divergence-free field. In [4], the authors suggest using a Fortran library POIS3D to solve the resulting Poisson equation directly, but [1] suggests instead solving it as a sparse linear system. We chose to follow the latter path, but had a hard time getting a solver to work correctly. Initially, we weren't sure of the properties of the matrix, and decided on the general biconjugate gradient descent approach described in [3]. Unfortunately, we couldn't get the linbcg solver to work under ANY conditions, even with simple 3x3 matrices. We then studied the problem more and realized our linear system was both symmetric and positive definite. This enabled us to use a simpler conjugate gradient solver that worked fine and produced accurate projections.

We toyed with the idea of allowing arbitrary boundaries within the grid and having the smoke be strictly maintained within a box or passing around objects, but never got it working. The papers discuss these cases and suggest setting the velocity in the normal direction of the surface to zero, but don't mention how this affects the projection step. Clearly it must: if it didn't, setting the velocities to zero before projecting would let the non-zero velocities bleed through the boundaries, but setting them to zero after would mean the field was no longer divergence-free. Therefore, the projection step must somehow be accomplished with the added constraint that the boundaries remain zero. This might not be difficult, but we weren't able to get it to work in the limited time we had for the project.

It goes without saying that we encountered numerous (sometimes comical) bugs along the way. One of the toughest to fix was that huge velocities would sometimes sweep into the air current grid, wildly tossing the smoke about.  The final video of our "results" section illustrates this bug. We believe that the problem was along the boundary of the grid. We solved it by setting all velocities to zero at the boundary voxels both before and after the projection step. This prevented any paths from being traced out of the grid during advection, essentially creating a 1-voxel buffer between the inside and the outside of the grid. All the simulations in the "results" section, except for the last, use this safety buffer.

Lastly, we spent a lot of time trying to make the smoke's motion realistic. The problem is that our simulation often looked "damp." That is, the smoke did not twist and curl enough. While the physics do accurately model the behavior of smoke, real smoke is never seen in a vacuum and is always interacting with random wind and noise. Moreover, the implicit solution methods we employed, while numerically stable, suffer from numerical dissipation, making the problem even worse.

We addressed this issue in two ways. First, at each time step we added small, random wind velocities. The first and second videos in our "results" section show this. The first simulation was conducted without any random velocities, while in the second, we initialized grid with random velocities and subsequently injected small, random velocities at each time step. The result is much more natural looking smoke: one does not usually experience smoke in a perfectly still room. Our second solution was to add a vorticity confinement force at each time step, as in [1].  This force is computed from the velocity grid using the "curl" operator. It is consistent with the physics in the sense that it disappears as the spacing between grid points goes towards zero. The third and fourth videos illustrate the difference between simulating without and with vorticity confinement, respectively. We did not simulate vorticity confinement in 2D, as the "curl" operator is only defined in 3D.




Results:

The following are some movies that we created during this project. To play them you will need a Quicktime player.

The first movie is a 2D smoke simulation. Here, we continuously inject smoke at three points in the bottom of the frame. We do not use vorticity confinement. The air field initially has zero velocity at all points, and we do not add random velocity during the simulation. Therefore the smoke rises perfectly smoothly and symmetrically.

2D still smoke
( high-res Quicktime ~11.0MB) ( low-res Quicktime ~0.4MB )



The following video is identical to that above, except that air field initially starts with random velocities, and we inject small random velocities into the air field at every time step. This yields the more chaotic and natural-looking smoke.

 
smoke gun
( high-res Quicktime ~38MB) ( low-res Quicktime ~3.2MB )


This video is analogous to the first video of the results section, except in 3D. The air field initially starts out with zero velocity, and we do not inject random velocities during the simulation. Instead of injecting smoke at three points, we continuously inject a ball of smoke near the floor. As in the first video, the rise of smoke is both smooth and symmetric.

smoke gun
( high-res Quicktime ~2.0MB) ( low-res Quicktime ~0.6MB )


This next video is like the last except that the air field starts off with initial random velocities. Also, we have added vorticity confinement, which makes the smoke twist and curl more naturally.

smoke gun
( high-res Quicktime ~3.0MB) ( low-res Quicktime ~1.0MB )


Here, we inject smoke at the bottom, slightly off-center. We also give the smoke an initial horizontal velocity, giving the effect of a "smoke gun." This simulation uses initial random velocities and vorticity confinement.

smoke gun
( high-res Quicktime ~3.2MB) ( low-res Quicktime ~0.9MB )


Finally, we present a "bloopers" video. At this point we were trying to do object intersection with an invisible sphere. Somehow, strong velocities enter the simulation half-way through, and the result is... well you can see below. We go into further detail about this bug in the "discussion" section.

smoke gun
( high-res Quicktime ~2.3MB) ( low-res Quicktime ~0.8MB )


References:

[1] R. Fedkiw, J. Stam, and H. W. Jensen. Visual Simulation of Smoke. In SIGGRAPH 2001 Conference Proceedings, Annual Conference Series , pages 15-22, August August 2001

[2] N. Foster and D. Metaxes. Modeling the Motion of a Hot, Turbulent Gas. In SIGGRAPH 97 Conference Proceedings, Annual Conference Series , pages 181-188, August 1997

[3] W. Press, S. Teukolsky, W. Vetterling, and B. Flannery. Numerical Recipes in C. Cambridge,  England: Cambridge University Press,  1992.

[4] J. Stam. Stable Fluids. In SIGGRAPH 99 Conference Proceedings, Annual Conference Series , pages 121-128, August 1999