32. OpenGL Particle Engine

Today we are going to be working with particle engines. The one here
is pretty basic but extremely easy to use and expand on. I have even
added texture masking for weirdly shaped particles. But unfortunately
I am yet to change the color of these on-the-fly without disrupting the
masking so for the moment, you cannot change the color to a number such as
0.1, 0.2, 0.3… it must be a whole number. Eg: 1 or 0.

Anyway, lets get on with the tutorial.

————————————————————————
Section 1: Variables

First off, we need to declare our header files. For this project we need
#include <GL/gl.h>
#include <GL/glut.h>
#include <windows.h>
#include <stdio.h>
#include <math.h>

This first variable I am going to use to hold the number of particles
we will display on the screen at any one time.
const ParticleCount = 500

Of course we also need something to hold our textures.
GLfloat texture[10];

Now here, I am creating a Type called PARTICLES which will hold all
our information for our particles.
typedef struct
{

The X position
double Xpos;
The Y position
double Ypos;
The Z position
double Zpos;
The movement on the X axis while being displayed
double Xmov;
The movement on the Z axis while being displayed
double Zmov;
The amount of Red within the object
double Red;
The amount of Green within the object
double Green;
The amount of Blue within the object
double Blue;
The angle of rotation
double Direction;
How fast it accelerates upwards
double Acceleration;
How fast it decelerates downwards
double Deceleration;
How much we wish to scale it
double Scalez;
}PARTICLES;

And now we will create a variable that will hold each of the PARTICLES
information for each actual Particle
PARTICLES Particle[ParticleCount];

————————————————————————
Section 2: Creation

To create the particles I am going to call the function glCreateParticles
during the initialization section of the code with:
glCreateParticles

Now for the creation function:
void glCreateParticles (void) {
This will be a temporary variable to cycle through our particles
int i;
We are going to loop through our particles until we reach the end
for (i = 1; i < ParticleCount; i++)
{

Set the inital X position to 0
Particle[i].Xpos = 0;
Set the inital Y position to -5
Particle[i].Ypos = -5;
Set the inital Z position to -5
Particle[i].Zpos = -5;
Set the amount of movement on the X axis to a random number, we dont want
all our particles doing the same thing 😛
Particle[i].Xmov = (((((((2 – 1 + 1) * rand()%11) + 1) – 1 + 1) * rand()%11) + 1) * 0.005) – (((((((2 – 1 + 1) * rand()%11) + 1) – 1 + 1) * rand()%11) + 1) * 0.005);
Set the amount of movement on the Z axis to a random number, as above, we dont
want all our particles doing the same thing 😛
Particle[i].Zmov = (((((((2 – 1 + 1) * rand()%11) + 1) – 1 + 1) * rand()%11) + 1) * 0.005) – (((((((2 – 1 + 1) * rand()%11) + 1) – 1 + 1) * rand()%11) + 1) * 0.005);
Set the amount of Red to 1
Particle[i].Red = 1;
Set the amount of Green to 1
Particle[i].Green = 1;
Set the amount of Blue to 1
Particle[i].Blue = 1;
Scale the particle to 1 quarter of its original size
Particle[i].Scalez = 0.25;
Set the initial rotation angle to 0
Particle[i].Direction = 0;
Set the amount of acceleration to a random number so they climb to different
heights
Particle[i].Acceleration = ((((((8 – 5 + 2) * rand()%11) + 5) – 1 + 1) * rand()%11) + 1) * 0.02;
Decrease their acceleration by 0.0025. They will slow down at a constant
rate but you will not see a difference
Particle[i].Deceleration = 0.0025;
}
}

—————————————————————————-
Section 3: Updating the Particles

The particles would not be effective, if they stayed at their inital state, so
we want them to change and acctually move. I am doing this by calling:
glUpdateParticles in my Display function.

void glUpdateParticles (void) {
Once again, another temporary variable to handle the cycle through the particles
int i;
Loop through all of the particles
for (i = 1; i < ParticleCount; i++)
{

Set the color of the current particle
glColor3f (Particle[i].Red, Particle[i].Green, Particle[i].Blue);

Move the particle on the Y axes, adding on the amount of acceleration
and then subtracting the rate of deceleration
Particle[i].Ypos = Particle[i].Ypos + Particle[i].Acceleration – Particle[i].Deceleration;
Increase the deceleration rate so the particle falls gaining speed
Particle[i].Deceleration = Particle[i].Deceleration + 0.0025;

Move the particle on the X axis
Particle[i].Xpos = Particle[i].Xpos + Particle[i].Xmov;
Move the particle on the Z axis
Particle[i].Zpos = Particle[i].Zpos + Particle[i].Zmov;

Rotate the particle
Particle[i].Direction = Particle[i].Direction + ((((((int)(0.5 – 0.1 + 0.1) * rand()%11) + 1) – 1 + 1) * rand()%11) + 1);

Now here I am saying that if the particle goes beneath its initial height
which I set earlier to -5, then it will restart the particle changing some
of the variables.
if (Particle[i].Ypos < -5)
{

Set the X position
Particle[i].Xpos = 0;
Set the Y position
Particle[i].Ypos = -5;
Set the Z position
Particle[i].Zpos = -5;
Set the amount of Red
Particle[i].Red = 1;
Set the amount of Green
Particle[i].Green = 1;
Set the amount of Blue
Particle[i].Blue = 1;
Set the angle of rotation
Particle[i].Direction = 0;
Adjust the Acceleration rate to another random number
Particle[i].Acceleration = ((((((8 – 5 + 2) * rand()%11) + 5) – 1 + 1) * rand()%11) + 1) * 0.02;
Reset the Deceleration rate
Particle[i].Deceleration = 0.0025;
}
}
}

—————————————————————————-
Section 4: Drawing the Particles

To draw the particles, I am calling the following function: glDrawParticle
from within the Display function, after the updating function.

void glDrawParticles (void) {
Yet another temporary variable for our cycles
int i;
Looping through our particles again
for (i = 1; i < ParticleCount; i++)
{

Distinguish the start of our current particle, we do not wish for them
all to be affected by the ones prior
glPushMatrix();

Translate the particle on the X, Y and Z axis accordingly
glTranslatef (Particle[i].Xpos, Particle[i].Ypos, Particle[i].Zpos);

Rotate the particle
glRotatef (Particle[i].Direction – 90, 0, 0, 1);
Scale the particle
glScalef (Particle[i].Scalez, Particle[i].Scalez, Particle[i].Scalez);

Disable Depth Testing so our masking appears as one
glDisable (GL_DEPTH_TEST);
Enable blending
glEnable (GL_BLEND);

Set the blending function to Take our Destination Colour and combine it with
Zero which is Black
glBlendFunc (GL_DST_COLOR, GL_ZERO);
Bind our mask
glBindTexture (GL_TEXTURE_2D, texture[0]);

Draw our shape

glBegin (GL_QUADS);
glTexCoord2d (0, 0);
glVertex3f (-1, -1, 0);
glTexCoord2d (1, 0);
glVertex3f (1, -1, 0);
glTexCoord2d (1, 1);
glVertex3f (1, 1, 0);
glTexCoord2d (0, 1);
glVertex3f (-1, 1, 0);
glEnd();

Then set out blending function to combine White with White
glBlendFunc (GL_ONE, GL_ONE);
Bind our texture
glBindTexture (GL_TEXTURE_2D, texture[1]);

Draw the shape

glBegin (GL_QUADS);
glTexCoord2d (0, 0);
glVertex3f (-1, -1, 0);
glTexCoord2d (1, 0);
glVertex3f (1, -1, 0);
glTexCoord2d (1, 1);
glVertex3f (1, 1, 0);
glTexCoord2d (0, 1);
glVertex3f (-1, 1, 0);
glEnd();

Re-enable Depth Testing
glEnable(GL_DEPTH_TEST);

End the changes to the current object
glPopMatrix();

}
}
}

————————————————————————-

And there we have it, a simple particle engine that pumps out 500 particles
at a steady framerate of 80 frames per second.

If you have any questions, feel free to email me at swiftless@gmail.com

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.

    #include <GL/gl.h>
#include <GL/glut.h>
#include <windows.h>
#include <stdio.h>
#include <math.h>

GLfloat texture[10];

const ParticleCount = 500;

typedef struct
{
double Xpos;
double Ypos;
double Zpos;
double Xmov;
double Zmov;
double Red;
double Green;
double Blue;
double Direction;
double Acceleration;
double Deceleration;
double Scalez;
bool Visible;
}PARTICLES;

PARTICLES Particle[ParticleCount];

GLuint LoadTextureRAW( const char * filename, int width, 
int height);
void FreeTexture( GLuint texturez );

void square (void) {
    glBindTexture( GL_TEXTURE_2D, texture[0] );
    glBegin (GL_QUADS);
    glTexCoord2d(0.0,0.0);
    glVertex2d(1.0,1.0);
    glTexCoord2d(1.0,0.0);
    glVertex2d(1.0,1.0);
    glTexCoord2d(1.0,1.0);
    glVertex2d(1.0,1.0);
    glTexCoord2d(0.0,1.0);
    glVertex2d(1.0,1.0);
    glEnd();
}

void glCreateParticles (void) {
int i;
for (i = 1; i < ParticleCount; i++)
{
Particle[i].Xpos = 0;
Particle[i].Ypos = 5;
Particle[i].Zpos = 5;
Particle[i].Xmov = (((((((2  1 + 1) * rand()%11) + 1)  1 + 1) *
rand()%11) + 1) * 0.005) (((((((2  1 + 1) * rand()%11) + 1)  1 + 1
) * rand()%11) + 1) * 0.005);
Particle[i].Zmov = (((((((2  1 + 1) * rand()%11) + 1)  1 + 1) *
rand()%11) + 1) * 0.005) (((((((2  1 + 1) * rand()%11) + 1)  1 + 1
) * rand()%11) + 1) * 0.005);
Particle[i].Red = 1;
Particle[i].Green = 1;
Particle[i].Blue = 1;
Particle[i].Scalez = 0.25;
Particle[i].Direction = 0;
Particle[i].Acceleration = ((((((8  5 + 2) * rand()%11) + 5
)  1 + 1) * rand()%11) + 1) * 0.02;
Particle[i].Deceleration = 0.0025;
}
}

void glUpdateParticles (void) {
int i;
for (i = 1; i < ParticleCount; i++)
{

glColor3f (Particle[i].Red, Particle[i].Green,
Particle[i].Blue);

Particle[i].Ypos = Particle[i].Ypos + Particle[i]
.Acceleration Particle[i].Deceleration;
Particle[i].Deceleration = Particle[i].Deceleration +
0.0025;

Particle[i].Xpos = Particle[i].Xpos + Particle[i].Xmov;
Particle[i].Zpos = Particle[i].Zpos + Particle[i].Zmov;

Particle[i].Direction = Particle[i].Direction + ((((((int
)(0.5  0.1 + 0.1) * rand()%11) + 1)  1 + 1) * rand()%11) + 1);

if (Particle[i].Ypos < 5)
{
Particle[i].Xpos = 0;
Particle[i].Ypos = 5;
Particle[i].Zpos = 5;
Particle[i].Red = 1;
Particle[i].Green = 1;
Particle[i].Blue = 1;
Particle[i].Direction = 0;
Particle[i].Acceleration = ((((((8  5 + 2) * rand()%11) + 5
)  1 + 1) * rand()%11) + 1) * 0.02;
Particle[i].Deceleration = 0.0025;
}

}
}

void glDrawParticles (void) {
int i;
for (i = 1; i < ParticleCount; i++)
{
glPushMatrix();

    glTranslatef (Particle[i].Xpos, Particle[i].Ypos, Particle[i].Zpos);
    glRotatef (Particle[i].Direction  90, 0, 0, 1);
   
    glScalef (Particle[i].Scalez, Particle[i].Scalez, Particle[i].Scalez);
   
    glDisable (GL_DEPTH_TEST);
    glEnable (GL_BLEND);
        
    glBlendFunc (GL_DST_COLOR, GL_ZERO);
    glBindTexture (GL_TEXTURE_2D, texture[0]);

    glBegin (GL_QUADS);
    glTexCoord2d (0, 0);
    glVertex3f (1, 1, 0);
    glTexCoord2d (1, 0);
    glVertex3f (1, 1, 0);
    glTexCoord2d (1, 1);
    glVertex3f (1, 1, 0);
    glTexCoord2d (0, 1);
    glVertex3f (1, 1, 0);
    glEnd();
    
    glBlendFunc (GL_ONE, GL_ONE);
    glBindTexture (GL_TEXTURE_2D, texture[1]);
    
    glBegin (GL_QUADS);
    glTexCoord2d (0, 0);
    glVertex3f (1, 1, 0);
    glTexCoord2d (1, 0);
    glVertex3f (1, 1, 0);
    glTexCoord2d (1, 1);
    glVertex3f (1, 1, 0);
    glTexCoord2d (0, 1);
    glVertex3f (1, 1, 0);
    glEnd();
        
    glEnable(GL_DEPTH_TEST);

glPopMatrix();

}
}

void display (void) {
    glClearDepth (1);
    glClearColor (0.0,0.0,0.0,1.0);
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();  
    glTranslatef (0,0,10);
    glUpdateParticles();
    glDrawParticles();
    glutSwapBuffers();
}

void init (void) {
    glEnable( GL_TEXTURE_2D );
    glEnable(GL_DEPTH_TEST);

    glCreateParticles();

    texture[0] = LoadTextureRAW( “particle_mask.raw”,256,256
); //load our texture
    texture[1] = LoadTextureRAW( “particle.raw”,256,256);
//load our texture
}

void reshape (int w, int h) {
    glViewport (0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective (60, (GLfloat)w / (GLfloat)h, 1.0, 100.0);
    glMatrixMode (GL_MODELVIEW);
}

int main (int argc, char **argv) {
    glutInit (&argc, argv);
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize (500, 500);
    glutInitWindowPosition (100, 100);
    glutCreateWindow (“A basic OpenGL Window);
    init();
    glutDisplayFunc (display);
    glutIdleFunc (display);
    glutReshapeFunc (reshape);
    glutMainLoop ();
    return 0;
}

//function to load the RAW file

GLuint LoadTextureRAW( const char * filename, int width, 
int height )
{
  GLuint texture;
  unsigned char * data;
  FILE * file;

  file = fopen( filename, “rb” );
  if ( file == NULL ) return 0;

  data = (unsigned char *)malloc( width * height * 3 );

  fread( data, width * height * 3, 1, file );
  fclose( file );

  glGenTextures(1, &texture );            

  glBindTexture(GL_TEXTURE_2D, texture);

  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 
GL_REPEAT);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 
GL_REPEAT);

  glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, 
GL_MODULATE );

  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
 GL_LINEAR_MIPMAP_NEAREST );

  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
 GL_LINEAR );

  gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, 
GL_RGB, GL_UNSIGNED_BYTE, data);

  free( data );

  return texture;
}

void FreeTexture( GLuint texture )
{
  glDeleteTextures( 1, &texture );
}

Download Texture(.RAW file)

Download Texture Mask(.RAW file)

Download Texture(.BMP file)

Download Texture Mask(.BMP file)

  • March 25, 2010
  • 15