Tutorial 1 - How to Make Fire
Magical spells or special effects can look good in games. In this tutorial I want
to show you how you can include effects like fire and smoke into a game using
How it Works
The burning fire is a particle system that emits particles (the flames). Each particle is a
quad that always faces the camera and each particle has an age of 0% to 100%. You can make
the particle system a bit more fancy but I will make it so each particle simply moves
in an upward direction; You might find experimenting with random movement could make
the particle fire more interesting.
We will apply 3 textures, flame coloured(See Figure 2.), to each particle and blend them together over time. Also,
we will apply 3 opacity maps, textures, to each particle and blend them together as well. The opacity maps
are textures with alpha values of pixels so we can use the alpha values to control
how much of the other 3 flame textures are visible. For example, we can have a flame shaped
alpha texture to make the red-ish textures appear like flames.
So that's basically how the fire works. We have to use some vector math to make the quads
always face the camera. This method seems to work fine but if you do get a performance hit then
you could try doing the same thing but with 2d sprites instead. With the second method you could
render the particles to a surface and then draw the surface like an ordinary sprite. I find
the method used in this tutorial is simpler to code.
We will use HLSL to do the blending of one texture into another.
Let's start by looking at how the HLSL effect blends the flame textures together.
You can download the HLSL txt file here: Download HLSL1.fx
Here is the pixel shader:
float4 ps_Flames(VS_OUTPUT IN) : COLOR0
float4 color1 = tex2D(FireTex1Sampler, IN.tex0);
float4 color2 = tex2D(FireTex2Sampler, IN.tex0);
float4 color3 = tex2D(FireTex3Sampler, IN.tex0);
float4 color4 = tex2D(FireOpTex1Sampler, IN.tex0);
float4 color5 = tex2D(FireOpTex2Sampler, IN.tex0);
float4 color6 = tex2D(FireOpTex3Sampler, IN.tex0);
color.r = color1.r*fBlend1 + color2.r*fBlend2 + color3.r*fBlend3;
color.g = color1.g*fBlend1 + color2.g*fBlend2 + color3.g*fBlend3;
color.b = color1.b*fBlend1 + color2.b*fBlend2 + color3.b*fBlend3;
color.a = color4.a*fBlend1 + color5.a*fBlend2 + color6.a*fBlend3;
In the code above each of the colours, color1, color2 ... color6, each holds the colour
of a pixel in the corresponding textures. For example, color1
contains the pixel colour of FireTex1.
The sampler for FireTex1 is defined as:
sampler FireTex1Sampler = sampler_state
Texture = (FireTex1);
MinFilter = Linear; MagFilter = Linear; MipFilter = Linear;
AddressU = Wrap; AddressV = Wrap; AddressW = Wrap;
MaxAnisotropy = 16;
A sampler allows us to access the pixels of a texture; For example FireTex1Sampler, FireTex2Sampler etc.
(see ps_Flames() above)
fBlend1 is a float between 0.0f-1.0f that controls the amount to blend FireTex1 with the other
textures. If fBlend1 is 0.0f then FireTex1 is entirely invisible (Not blended).
fBlend2 is the amount to blend FireTex2 with the other textures.
Each particle needs to tell the shader how much to blend the textures. In other words
each particle needs to set the fBlend1, fBlend2 and fBlend3 when it is rendered.
It's not necessary to use a separate quad mesh for each particle; Instead each particle
can use the same mesh and change it's position and vertices.
I have defined the ParticleMesh class as follows:
void Init(IDirect3DDevice9* pDevice);
void MakeBillboard(D3DXVECTOR3 Pos, Camera* camera, float quad_width, float quad_height);
void Render(IDirect3DDevice9* pDevice, ID3DXEffect* pEffect);
void CreateVertices(IDirect3DDevice9* pDevice);
void UpdateVertices(D3DXVECTOR3 TopLeft,
The Init() method calls CreateVertices() to create the actual quad.
MakeBillboard() re-arranges the 4 vertices to produce a billboard of the
given width and height(quad_width and quad_height).
Render() is intended to be called by an individual particle to render the billboard.
You can examine the MakeBillboard() function if you want to know how a billboard
is produced. I won't go over the maths but basically you just need to
understand normalized vectors and cross products. See this tutorial on
The Particle class is defined as:
class PSystem;//For friend reference.
friend class PSystem;
//render_depth used to sort order of rendering.
void SetParticleMesh(ParticleMesh* quad, float particle_size);
void Update(float deltaTime);
//camera used for billboarding.
void Render(IDirect3DDevice9* pDevice, ID3DXEffect* pEffect, Camera* camera);
//blend amounts in range of 0 to 1
void SetParticleSize(float size);
I've added some comments to help you understand the code.
For this tutorial I will use a simple particle emitter(class PSystem). The particle emitter
stores particles in a std::vector. If a particle is dead, i.e. age > 100%, then the
emitter removes that particle from the std::vector.
The PSystem::Update() function calls Update() on all particles that are alive. This indirectly
updates the position of each particle and the fBlend1, fBlend2 and fBlend3 for each particle.
The Emitter emits particles from a circle area.
The particle emitter is defined as follows:
void Init(ID3DXEffect* pEffect, IDirect3DDevice9* pDevice);
void Update(IDirect3DDevice9* pDevice, float deltaTime, Camera* camera);//Spawns particles while SpawnTimeElapsed is < TotalSpawnTime.
void StartSpawn(D3DXVECTOR3 Pos,
void SortParticlesByDepth(Camera* camera);
void RenderParticles(IDirect3DDevice9* pDevice, ID3DXEffect* pEffect, Camera* camera);
float SpawnTimeElapsed;//Set this to zero in a call to StartSpawn().
Mesh* pGround;//This is for spawning particles from the ground.
Again I think the best way to understand the PSystem is to examine the code. Look at all
occurences of particle_system1.
Basically it sets the 3 flame textures we want to use in the HLSL Effect(Uploads them to the Effect). And it sets the
opacity textures likewise. The PSystem::Init() function loads the textures from files.
You can create an instance of the PSystem like this:
//(In the Game class or a suitable place)
Initialize the PSystem and start spawing particles:
particle_system1.StartSpawn(D3DXVECTOR3(-20,-10,20),//Start spawning at origin of world.
5.0f,//Particles per second.
10000.0f,//Total Spawn time.
To view the PSystem in it's entirety, take a look at the whole application code.
Download Complete Source
Note: If you download the Application Zip make sure you unzip all the files to the same
directory before trying to run the program.
I hope you enjoyed this tutorial.