Fire

Fire


I wasn't really sure weather to put this one under Graphics or Models. In the end, I chose Models because I felt like it. Fire is an extremely hard thing to simulate accurately on a computer because it is a huge monster 3D fluid dynamic system. To produce an accurate simulation would require this much (arms stretched wide) computing power, and would certainally not be possible in real time. Moreover, I haven't the foggiest idea how to do it. It is for these reasons that I am not going to describe a super accurate model.
Most of you will have seen the 2D fire effect that was shown off in many demos, and is still lingering around. I am going to describe and explain this system first, then explain how this can be taken furthur to create a more pleasing flame.

You can download the fire I wrote on the left. My apologies to owners of slower computers. Although it's been optimised a bit (to about twice the speed), it's not yet really fast. It should run fine on a pentium. It requires DOS/4GW.
source

Please note that, being a PC programmer, I tend to make this kind of thing in VGA 320x200 screen mode with 256 colours. So I tend to use the words pixel and byte interchangably.


The idea behind the origional fire effect was simple. The bottom of the screen was constantly seeded with 'hot' pixels. On each time step, the image was smoothed, darkened and scrolled up one pixel. These three simple operations, combined with the speckled pixels at the bottom, and an orangey palette, creates what undoubtedly looks like a flame. Some implementations looked very good, others just looked like a characterless fire effect. With a little thought, the fire can be made quite complex, producing those nice toungs of flame you get in real fires. On this page, I will show you how to make fire with uneven cooling, and convection effects. Because the fire is a realtime effect, I will be including source code here in (slightly) Pentium optimised assembler, but not without explaining the process in English and pseudo-code first.


There are several parts to a fire effect. All these are done in an offscreen buffer, which is then copied over to the screen. The buffer typically consists of an array of bytes.

1. The Heat Source
The flames can be made to start anywhere on the screen by simply sprinkling a few pixels. Most implementations had the flame source at the bottom of the screen. You can make the flames stronger or weaker by plotting lighter or darker pixels respectively. You can make the flames smoother or more flickery by plotting more or less pixels respectively.

2. Heat Spread

The next step is to smooth the image. There are a few ways to do this depending on exactly how you want the image to smooth. I tried to be really crafty about it, working things out really mathematically, but in the end, the effect was rubbish. The simplest ways are the best. You can smooth the image by making each pixel equal to the average of it's four neighbours.
                pixel(x,y) = (pixel(x,y-1) + pixel(x,y+1) + pixel(x-1,y) + pixel(x+1,y))
                             -----------------------------------------------------------
                                                        4
Simple and fast. I will explain other smoothing techniques later which allow you to make the convection proportional to the temperature of the pixel.

3. Cooling

The cooling is simple and involves subtracting a small amount from every pixel. The exact amount depends on how quickly you want the fire to cool. Watch out for wrap around. Make sure the value of the pixel does not go below zero.

4. Convection

The basic convection model just scrolls the image up by a pixel or two.


So that's the basic idea behind the flame effect. Those of you who are even remotely alert will have noticed that the algorithm described above could be made significantly faster by combining the smoothing and convection into one. You are indeed correct. Any programmer worth their salt should see that, so that is where I am going to leave it. What I want to explain is how to make the fire look really good. (And incidently in this next method the smoothing and convection are indeed both done at once).


Uneven Cooling

The problem with the method above is that the fire tends to look rather boring. The flames rise linearly, and the whole system cools at exactly the same rate. Any unevenness in the fire depends on plotting random pixels.
One way to give your fire those nice tounges of flame, without relying on careful pixel sprinkling is to make the flames cool unevenly. Since these flames are to be done in realtime, your best bet is to create a cooling map.

Cooling Map

Create an array of bytes the same size as the screen, and cover it with some smooth pattern. A good way to create the pattern is to sprinkle a few thousand pixels of various intensities onto the screen, then apply several smoothing passes to it (about 20 is good). This map should be quite dark, intensities ranging from 0 to about 10.
Now, rather than subtracting the same small amount from every pixel, values from the cooling map can be used instead. so:
        pixel(x,y) = pixel(x,y) - coolingmap(x,y)

Dark areas on the map will produce bigger flames, and light areas cool the flames quickly. The best results can be achieved by scrolling the cooling map upwards at the same speed as the convection. Obviously, if you were to actually program this, you would not physically scroll the cooling map every frame, but access it by some vertical offset.

The cooling map actually defines the shape of the flames. You needn't restrict yourself to a randomly generated map. You can add simple shapes, (pentagrams, skulls, acid house smilies) to the map. and see them created in flame. The shapes should have low pixel values, and be surrounded by higher value pixels.


Right, so now we've got that out of the way, it's time for some pseudocode.


INTEGER: xsize
INTEGER: ysize
INTEGER: c
INTEGER: n1
INTEGER: n2
INTEGER: n3
INTEGER: n4
INTEGER: p
ARRAY_OF_BYTES: buffer1(xsize*ysize)
ARRAY_OF_BYTES: buffer2(xsize*ysize)
ARRAY_OF_BYTES: CoolingMap(xsize*ysize)

loop forever

  loop y  from 1 to (ysize-2)                   ;Loop through all pixels on the screen, except
    loop x  from 1 to (xsize-2)                 ;the ones at the very edge.

      n1 = read pixel from buffer1(x+1, y)      ;Read the 4 neighbouring pixels
      n2 = read pixel from buffer1(x-1, y)
      n3 = read pixel from buffer1(x, y+1)
      n4 = read pixel from buffer1(x, y-1)

      c  = read pixel from CoolingMap(x, y)     ;Read a pixel from the cooling map

      p = ((n1+n2+n3+n4) / 4)                   ;The average of the 4 neighbours
      p = p-c                                   ;minus c

      if p<0 then p=0                           ;Don't let the fire cool below zero

      write pixel of value p to buffer2(x,y-1)  ;write this pixel to the other buffer
                                                ;notice that it is one pixel higher.

    end x loop
  end yloop

  copy buffer2 to the screen                    ;Display the next frame
  copy buffer2 to buffer1                       ;Update buffer1
  scroll CoolingMap up one pixel

end of loop


The finishing touch
There is still something missing, life. The flames rise up from their source at a constant speed. It's as if the fire was burning in space. There's no air. Look at a real fire and you will see it swirling and flickering. We can add this to our fire.
All that's needed is some kind of constantly changing flow of air. As is happens, you will find an article on just this in the graphics section. It is called Warp Feedback.


More Effects
Careful placing of the seed pixels can make a fire effect look really good. Rather than just sprinkling then randomly at the bottom of the screen, try doing other things.

Sparks

You can have a few pixels rising up out of the fire, creating little glowing dots like sparks. In turn, these can pop, giving off a couple more sparks. With time they can dim.

Fire Balls

Try throwing the pixels around in a clump. They will move around the screen, leaving a flaming trail. Make them explode when they hit something. The effect can be quite impressive.
On a smaller scale, the fire can even be used as a flame thrower effect for a game. Throw pixels out of the gun, creating a stunning BBQ weapon.