In this tutorial I will show you how to implement a basic particle system. Particle systems can be used for many different usefull applications like fountains, waterfalls, fireworks and many other things. As a simple example I will show you how to simulate a fountain with a particle system. The nice thing with this example is, that it shows most of the stuff you usually need, when you want to implement anything with a particle system.
Let's start with a short discussion of the physics:
When we have a waterfall the physics are pretty simple: the water is in a certain height and falls down into the lake or river. If we take a water drop on top of the water fall (immediately before it falls down), it has a horizontal velocity - let's call it v1 - due to the flow speed of the river. The vertical speed v2 is in this moment zero, since the water drop hasn't started to fall. When the water drop reaches the lake, all the potential energy it had on top is converted into kinetic energy (i.e. converted into motion energy). It's vertical speed v2' is then sqrt(2*g*h). In a simplified simulation you can use this speed as the speed of the water drops. It is simplified because the speed used in this simulation is not a proper description of the current speed. However it is accurate in so far that the water of higher waterfalls has a higher end velocity than the water of small waterfalls. The horizontal speed is almost constant during the fall, so for all practical purposes you can keep this veolcity constant.
The simulation of the water drops in fountain can be separated in two parts: first the water drops rise with a certain initial speed v1 but this speed decreases continously due to gravity which tries to pull down the water drop until it reaches it's highest point, where it's vertical speed becomes zero. At this point the second part starts which is identical to the "waterfall" we just discussed. The horizontal speed v2 is more complicated in this case, since we can't take the flow speed of the river. In the case of a fountain the horizontal speed component has two reasons: first of all the water comes out of a pipe, where it was under pressure which means, that there was a force towards the walls of the pipe and this force directly translates into a horizontal speed component. Nevertheless in a typical fountain as you can see it in a park or pond, the dominant component of the horizontal speed doesn't come from this pressure induced velocity, but from the speed due to the nozzle at the end of the pipe.
The simulation of fireworks is pretty straight forward: first you select a number of particles - let's say 200. During the start phase (the rocket phase) we use almost the same coordinates for all the particles. The speed of the rocket is determined by the initial speed reduced by the influence of gravity (i.e. the vertical speed is reduced by gravity until the rocket would stop rising and starts falling down). When the rocket explodes things become more interesting: additionally to the original vertical and horizontal speed components each partical gets one speed component which is directed away from the explosion. It is not sufficient to replace the original two speed components by the speed component from the explosion! In this case you would neglect the influence of gravity and the original trajectory of the rocket, which would result in a very unrealistic simulation).
For this simple example I use a structure with static variable definition. However in a game or bigger simulation I would suggest, that you use a class for the particle system. The structure I use for the particle type is defined in the following way:
typedef struct { float lifetime; // total lifetime of the particle float decay; // decay speed of the particle float r,g,b; // color values of the particle float xpos,ypos,zpos; // position of the particle float xspeed,yspeed,zspeed; // speed of the particle boolean active; // is particle active or not? } PARTICLE;
When the particle is created, the lifetime is set to a certain value. The decay is a number, that is subtracted with each evolution cycle from the lifetime and therefore makes sure, that the particle dies. Actually the decay factor is redundant, since the lifetime is everything you need to describe the life of a particle (you simply adjust the lifetime according to the difference in the decay factor). The reason for this redundancy is, that I originally planed another example with another structure for the particles and then simply forgot to remove it - I hope it doesn't confuse too much.
The r,g,b values determine the color of the particle, xpos, ypos, zpos, represents the current position of the particle and xspeed, yspeed, zspeed gives the current speed of the particle. Very usefull is also the active property of the particle. It can be used to instantly turn on or off the particle system . To initialize the particle system for the fountain example I use the following code:
void CreateParticle(int i) { particle[i].lifetime= (float)random(500000)/500000.0; particle[i].decay=0.001; particle[i].r = 0.7; particle[i].g = 0.7; particle[i].b = 1.0; particle[i].xpos= 0.0; particle[i].ypos= 0.0; particle[i].zpos= 0.0; particle[i].xspeed = 0.0005-(float)random(100)/100000.0; particle[i].yspeed = 0.01-(float)random(100)/100000.0; particle[i].zspeed = 0.0005-(float)random(100)/100000.0; particle[i].active = true; }
We give the particles a light blue color (after all they should represent water drops), all of them start at the position (0,0,0) which represents the end of the pipe and their x- and z-speed can be positiv or negative and is pretty small compared to the negative y-speed which determines the heigth of the fountain. When you increase the x- and z-speed, your fountain becomes wider. Afteer we have created the particles, we must determine it's evolution:
void EvolveParticle() { for(int i=0;i<=maxparticle;i++){ // evolve the particle parameters particle[i].lifetime-=particle[i].decay; particle[i].xpos+=particle[i].xspeed; particle[i].ypos+=particle[i].yspeed; particle[i].zpos+=particle[i].zspeed; particle[i].yspeed-=0.00007; } }
To decrease the lifetime, we simply subtract the decay factor from the lifetime, the new position is the old position plus the speed and finally we decrease the y-speed: this is the influence of gravity. Now our particles simulate the water drops in a fountain. The only problem is, that we can't see anything so far. When we want to draw the particles we have a couple of options: we can use OpenGL particles, quads, triangles or triangle stripes. I have chosen triangle stripes since it can be drawn very fast and gives more freedom than particles, where you can't use any textures.
void DrawObjects() { // rendering functions glLoadIdentity(); glRotatef(50.0,1.0,0.0,0.0); // show scene from top front glBindTexture(GL_TEXTURE_2D,ParticleTexture); // choose particle texture for (int i=0;i<=maxparticle;i++){ if(particle[i].ypos<0.0) particle[i].lifetime=0.0; if((particle[i].active==true) && (particle[i].lifetime>0.0)){ glColor3f(particle[i].r,particle[i].g,particle[i].b); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(0.0,1.0); glVertex3f(particle[i].xpos+0.002, particle[i].ypos+0.002, particle[i].zpos+0.0); // top right glTexCoord2f(0.0,0.0); glVertex3f(particle[i].xpos-0.002, particle[i].ypos+0.002, particle[i].zpos+0.0); // top left glTexCoord2f(1.0,1.0); glVertex3f(particle[i].xpos+0.002, particle[i].ypos-0.002, particle[i].zpos+0.0); // bottom right glTexCoord2f(1.0,0.0); glVertex3f(particle[i].xpos-0.002, particle[i].ypos-0.002, particle[i].zpos+0.0); // bottom left glEnd(); } else CreateParticle(i); } EvolveParticle(); }
What we have now is a simple but nice fountain:
When you look at the animation it looks much better then the still image. I hope this tutorial could show you the basics of a particle system and give you some ideas how you could use them in your own programs. As always, your comments and suggestions for improvements other applications etc. are very welcome.