Featured Posts

8. Bump Mapping in GLSL8. Bump Mapping in GLSL Introduction Bump mapping is essential in todays computer games, and computer graphics in general. Would you like to know the best thing about it? It is extremely simple to implement. Bump mapping works...

Read more

Swiftless GLSL Shader DeveloperSwiftless GLSL Shader Developer Swiftless GLSL Shader Developer   Version 0.1a Currently Swiftless GLSL Shader Developer is in it's first public release, and is currently in alpha status, meaning it is not complete and may contain...

Read more

Wordpress Optimization Wordpress Website Optimizations Introduction Wordpress itself is a fairly wonderful tool. Since switching to it, I find it is a lot quicker to make changes to my website and it is also quicker to get...

Read more

36. OpenGL Framebuffers36. OpenGL Framebuffers Introduction Frame buffers are one of those mythical things that we have all heard of, but many beginner OpenGL developers avoid because there is not much information about them, and they can be confusing...

Read more

1. Terrain Class1. Terrain Class Terrain is one of those things that so far, hasn't been perfectly recreated in computer graphics. But it is almost there! Looking over a beautiful landscape can be one of the most amazing feelings in the...

Read more

  • Prev
  • Next

36. OpenGL Framebuffers

Posted on : 22-04-2010 | By : Swiftless | In : OpenGL

Tags: , , , , , , , , ,

2

Introduction

Frame buffers are one of those mythical things that we have all heard of, but many beginner OpenGL developers avoid because there is not much information about them, and they can be confusing at first. A frame buffer can be thought of as another window that we render to, but we don’t end up seeing.

Now you might be thinking, if we draw something, but we don’t end up seeing it, then what is the point? Have you ever heard of off-screen rendering? I might ask you this instead, have you ever seen a reflection in a game of a live scene? Well, to get this reflection, we typically have to render the scene from the angle of the reflection first, and then we turn that render into a texture and bind it to whatever shape has the reflection attached to it. This initial rendering of the reflection is typically done in a frame buffer for speed, as we can bind a frame buffer straight to a texture.

If you have ever heard of pixel buffers, don’t get them confused with frame buffers, they are different, but can be used together. I won’t go into pixel buffers here, I might write a tutorial on them later on, but they are highly useful for asynchronous texture reads and writes. I once had a project, and part of it required I write to two frame buffers and read back the data, and then perform operations on this data and push it back to the GPU as a texture as quickly as possible. Using a combination of frame buffers and pixel buffers, I went from approximately 5 frames per second doing this, to 30 frames per second, and if the demand arises, I will post a tutorial on how to do this. It was a case where offloading the information to the GPU was not possible.

You should now have an idea of what frame buffers are, in short, they are a rendering context that can be bound to a texture, so let’s start coding them and see what we come up with. I am going to aim for a quad, with a texture of a rotating cube on it. It is a simple example, but highly useful, as you can imagine, the entire scene can be rendered into a frame buffer, and then placed onto a quad for some post processing.

Code

Frame buffer objects are stored like textures. OpenGL will store the information on the graphics card, and it will return an ID associated with that texture for us to place inside an unsigned integer. Also, as seen with textures, the frame buffer ID of 0 (zero) is reserved by OpenGL and is used to unbind the current frame buffer. Also, frame buffers can have several buffers attached. The default buffer is the colour buffer, but you can also bind depth buffers, stencil buffers, etc. So let’s create some variables to hold our frame buffer, frame buffers depth buffer and the texture we are going to store our frame buffer in.

unsigned int fbo; // The frame buffer object
unsigned int fbo_depth; // The depth buffer for the frame buffer object
unsigned int fbo_texture; // The texture object to write our frame buffer object to

After we have all the variables required to create and use our frame buffers (yes, we only need three variables), we are going to add some extra variables. First, we need two integer values for the width and height of our GLUT window, this is because when you create a frame buffer, you need to specify the width and the height of the texture, and when you use the frame buffer, you need to change the viewport size to match the texture size. Because frame buffers are independent of the size of the GLUT window, you can use them to create higher or lower resolution textures than your regular scene. Typically you would use a lower resolution texture for effects such as reflections so that it renders quicker. The last extra variable we are going to use will just hold how much our teapot in our frame buffer scene will rotate.

int window_width = 500; // The width of our window
int window_height = 500; // The height of our window

float rotation_degree = 0.0f; // The angle of rotation in degrees for our teapot

The next step we have to do is to create/initialize our frame buffer, the associated depth buffer, and the texture we are going to render to. Typically it is done in the following order:

  1. Create the Depth Buffer that we are going to use with our frame buffer.
  2. Create the Texture that we are going to bind our frame buffer to.
  3. Create the actual Frame Buffer.
  4. Bind the texture to the frame buffer.
  5. Bind the depth buffer to the frame buffer.
  6. Check for errors.

You can check for errors along the way if you wish, but I will just be using one check to make sure our final frame buffer is complete.

Frame Buffer – Depth Buffer

Let’s create the depth buffer we are going to use. To begin with, create a method call initFrameBufferDepthBuffer. This method is going to take no parameters and is not going to return anything. This method is going to contain all code related to the depth buffer we are going to use.

void initFrameBufferDepthBuffer(void) {

}

Inside of this method to create the depth buffer, the first call we need to make will create a render buffer for OpenGL to use, and is very similar to the code used to create textures.

glGenRenderbuffersEXT(1, &fbo_depth); // Generate one render buffer and store the ID in fbo_depth

This will create a render buffer, and store the ID in our fbo_depth variable for us to access.  Once the render buffer is created for the depth buffer, we then need to bind the buffer so that we can play with it and once we are finished with it, we need to bind the null render buffer, which takes the value of 0. When we are binding a frame buffer, it is once again very similar to texturing, so let’s place our binding code inside our method to fill between.

void initFrameBufferDepthBuffer(void) {
glGenRenderbuffersEXT(1, &fbo_depth); // Generate one render buffer and store the ID in fbo_depth
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo_depth); // Bind the fbo_depth render buffer
...
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); // Unbind the render buffer
}

Next, we need to fill in the … between the binding of the render buffers. These next two lines are the lines that tell OpenGL that this render buffer will be used for depth. The first line tells OpenGL that we are going to be storing the depth component, and we are going to monitor the entire size of the window. The next line then says that we are going to use fbo_depth to render the depth buffer/attachment.

glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, window_width, window_height); // Set the render buffer storage to be a depth component, with a width and height of the window

glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo_depth); // Set the render buffer of this buffer to the depth buffer

Frame Buffer – Texture

Recapping on what we have done, we have created a render buffer that will store the depth component of the buffer that it is attached to. Now we need to create the texture that we want to store our frame buffer in. To do this, let’s create another method similar to the one we just created, but I am going to call this one initFrameBufferTexture. Because we are only creating a texture here, I am not really going to explain this code, the code comments should outline what is going on if you don’t know about texturing in OpenGL (in which case, you are diving in fairly deep straight out).

void initFrameBufferTexture(void) {
glGenTextures(1, &fbo_texture); // Generate one texture

glBindTexture(GL_TEXTURE_2D, fbo_texture); // Bind the texture fbo_texture

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, window_width, window_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // Create a standard texture with the width and height of our window

// Setup the basic texture parameters

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

// Unbind the texture
glBindTexture(GL_TEXTURE_2D, 0);
}

Frame Buffer – Initialization

Finally, we need one more method; this one will be called initFrameBuffer and will make calls to the above methods we have created, as well as creating the frame buffer and attaching the texture and the depth buffer.

void initFrameBuffer(void) {
initFrameBufferDepthBuffer(); // Initialize our frame buffer depth buffer

initFrameBufferTexture(); // Initialize our frame buffer texture
…
}

After making the calls to our above methods, we need to create the frame buffer, and bind it. This is extremely similar to how we created the render buffer for the depth component, and how we create textures, so the following should look vaguely familiar.

glGenFramebuffersEXT(1, &fbo); // Generate one frame buffer and store the ID in fbo
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); // Bind our frame buffer
…
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Unbind our frame buffer

This code will generate one frame buffer, and then bind it so we can modify it, and then finally it will unbind it. This is fairly straight forward, and the next two calls will attach the texture and render buffer to our frame buffer. First off, let’s attach the texture to the frame buffer.

glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fbo_texture, 0); // Attach the texture fbo_texture to the color buffer in our frame buffer

Now that we have a texture, theoretically we can use this straight out, but you won’t get any depth information. So we need to go ahead and attach the depth render buffer we created called fbo_depth, to our frame buffer.

glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo_depth); // Attach the depth buffer fbo_depth to our frame buffer

And now we have a frame buffer, which outputs to a texture and also has a depth buffer, which we can use. Well, we think we do, so first let’s check if our frame buffer is complete and there were no problems. To do this, we can make a call to glCheckFramebufferStatusEXT while our frame buffer is bound, and this will give us back a GLenum with the status of our frame buffer. For our frame buffer to be complete, we need to check that the status is equal to GL_FRAMEBUFFER_COMPLETE_EXT. We can do this with a simple if statement. Note that if you want the output to the console, you need to include <iostream> into your file.

GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); // Check that status of our generated frame buffer

if (status != GL_FRAMEBUFFER_COMPLETE_EXT) // If the frame buffer does not report back as complete
{
std::cout << "Couldn't create frame buffer" << std::endl; // Make sure you include <iostream>
exit(0); // Exit the application
}

General Initialization

Because in this example, we want to use the frame buffer constantly, I am going to create an init method which will call the initFrameBuffer method, and init will be called straight after we initialize GLEW, which is straight after we call glutCreateWindow. So here is our init method, which in this example, enables texturing, enables depth testing, and then initializes our frame buffer.

void init(void) {
glEnable(GL_TEXTURE_2D); // Enable texturing so we can bind our frame buffer texture
glEnable(GL_DEPTH_TEST); // Enable depth testing

initFrameBuffer(); // Create our frame buffer object
}

Next up, here is an example of my main method, which calls glewInit and calls our own init method.

int main (int argc, char **argv) {
…
glutCreateWindow ("You’re first OpenGL Window"); // Set the title for the window

if (GLEW_OK != glewInit()) {
std::cout << "Couldn't initialize GLEW" << std::endl;
exit(0);
}

init();
…
}

Frame Buffer – Usage

To use our frame buffer, I am first going to give you the display method, and whilst you could put all your code in here, I am going to make a call to a method to render our teapot scene. This display method builds upon the tutorial for creating a square.

So it looks something like the following code, which will create a square with texture coordinates, and I have placed it back 2 units so that it fills up most of the screen. Keep in mind, that I am not doing anything with the frame buffer at the moment; this is just a standard display method.

void display (void) {
keyOperations();

glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // Clear the background of our window to red
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Clear the colour buffer (more buffers later on)
glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations

glTranslatef(0.0f, 0.0f, -2.0f);

glBegin(GL_QUADS);

glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner

glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner

glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner

glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner

glEnd();

glutSwapBuffers();
}

Now we will go ahead and bind our frame buffer texture to the quad. This is done just like binding a regular texture, because it is just a regular texture. The only difference between the texture we use for our frame buffer, and a standard texture we load in from a file, is that this texture is filled in on the GPU by OpenGL.

So right before your quad begins, bind your frame buffer texture, and then after you finish drawing your quad, unbind it.

glBindTexture(GL_TEXTURE_2D, fbo_texture);

glBegin(GL_QUADS);

…

glEnd();

glBindTexture(GL_TEXTURE_2D, 0);

You should be able to run this now, but unfortunately because we haven’t rendered anything into the frame buffer, nothing will appear on our quad, it will be as if we never bound a texture. So let’s create a method where we are going to render our frame buffer. I am going to call this renderTeapotScene, because I am using it to render a teapot.

void renderTeapotScene(void) {

}

renderTeapotScene is going to be called from inside our display method, right after our keyOperations and before we do any actual rendering.

void display(void) {
keyOperations();

renderTeapotScene();

...
}

Inside of our renderTeapotScene method, we need to first bind our frame buffer so that we can render to it, and then unbind it when we are finished rendering. I am also going to add in some extra code at the very end of this method, just to update the amount in which we are going to rotate our teapot.

void renderTeapotScene(void) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); // Bind our frame buffer for rendering

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Unbind our texture

rotation_degree += 0.5f;
if (rotation_degree > 360.0f)
rotation_degree = 0.0f;
}

So far, everything should be fine, except when you bind a frame buffer, you need to set the size of the viewport that the frame buffer will render to. This is done with a call to glViewport, but first I am going to do some more management orientated activities, and store the current state of our GL_ENABLE_BIT and our GL_VIEWPORT_BIT. This is so that when we finish, we can put these back to how they were and not having any glEnable or glViewport calls modify our regular rendering. It is also wise to store information such as GL_LIGHTING_BIT if you are using lighting, and any other states you might need.

void renderTeapotScene(void) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); // Bind our frame buffer for rendering
glPushAttrib(GL_VIEWPORT_BIT | GL_ENABLE_BIT); // Push our glEnable and glViewport states

glPopAttrib(); // Restore our glEnable and glViewport states
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Unbind our texture

rotation_degree += 0.5f;
if (rotation_degree > 360.0f)
rotation_degree = 0.0f;
}

It is our calls to glPushAttrib and glPopAttrib which will save and restore our states. The next step as I said is to set the viewport size for rendering, and then I am going to clear the colour buffer, depth buffer and reset the model view matrix, just like you would in your regular display method.

void renderTeapotScene(void) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); // Bind our frame buffer for rendering
glPushAttrib(GL_VIEWPORT_BIT | GL_ENABLE_BIT); // Push our glEnable and glViewport states
glViewport(0, 0, window_width, window_height); // Set the size of the frame buffer view port

glClearColor (0.0f, 0.0f, 1.0f, 1.0f); // Set the clear colour
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear the depth and colour buffers

glLoadIdentity();  // Reset the modelview matrix

glPopAttrib(); // Restore our glEnable and glViewport states
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Unbind our texture

rotation_degree += 0.5f;
if (rotation_degree > 360.0f)
rotation_degree = 0.0f;
}

This should be starting to look a little familiar inside of our frame buffer and push attributes calls, and you are probably right, everything you do from now on is just like rendering a normal scene, only you don’t need to call glFlush or glutSwapBuffers like you would with a regular render as we are not pushing the output to the screen. So now all we need to do is add the code in for our teapot rendering. I am going to translate it back five units, and then rotate it along the x and the y axis.

void renderTeapotScene(void) {
…
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear the depth and colour buffers
glLoadIdentity();  // Reset the modelview matrix

glTranslatef(0.0f, 0.0f, -5.0f); // Translate back 5 units

glRotatef(rotation_degree, 1.0f, 1.0f, 0.0f); // Rotate according to our rotation_degree value

glutSolidTeapot(1.0f); // Render a teapot

glPopAttrib(); // Restore our glEnable and glViewport states
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Unbind our texture
…
}

If you followed everything as I have said correctly, then you should be able to compile this and run it. You should then see a quad with a red background, with a texture of a spinning teapot with a blue background attached to the quad.

Well done if you managed that, and now have your head around frame buffers!

If you have any questions, you can always contact me at swiftless@gmail.com

Tutorial Code

#include <GL/glew.h> // Include the GLEW header file
#include <GL/glut.h> // Include the GLUT header file
#include <iostream> // Allow us to print to the console

bool* keyStates = new bool[256]; // Create an array of boolean values of length 256 (0-255)

unsigned int fbo; // The frame buffer object
unsigned int fbo_depth; // The depth buffer for the frame buffer object
unsigned int fbo_texture; // The texture object to write our frame buffer object to

int window_width = 500; // The width of our window
int window_height = 500; // The height of our window

float rotation_degree = 0.0f; // The angle of rotation in degrees for our teapot

void initFrameBufferDepthBuffer(void) {

glGenRenderbuffersEXT(1, &fbo_depth); // Generate one render buffer and store the ID in fbo_depth
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo_depth); // Bind the fbo_depth render buffer

glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, window_width, window_height); // Set the render buffer storage to be a depth component, with a width and height of the window

glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo_depth); // Set the render buffer of this buffer to the depth buffer

glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); // Unbind the render buffer
}

void initFrameBufferTexture(void) {
glGenTextures(1, &fbo_texture); // Generate one texture
glBindTexture(GL_TEXTURE_2D, fbo_texture); // Bind the texture fbo_texture

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, window_width, window_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // Create a standard texture with the width and height of our window

// Setup the basic texture parameters
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

// Unbind the texture
glBindTexture(GL_TEXTURE_2D, 0);
}

void initFrameBuffer(void) {
initFrameBufferDepthBuffer(); // Initialize our frame buffer depth buffer

initFrameBufferTexture(); // Initialize our frame buffer texture

glGenFramebuffersEXT(1, &fbo); // Generate one frame buffer and store the ID in fbo
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); // Bind our frame buffer

glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fbo_texture, 0); // Attach the texture fbo_texture to the color buffer in our frame buffer

glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo_depth); // Attach the depth buffer fbo_depth to our frame buffer

GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); // Check that status of our generated frame buffer

if (status != GL_FRAMEBUFFER_COMPLETE_EXT) // If the frame buffer does not report back as complete
{
std::cout << "Couldn't create frame buffer" << std::endl; // Output an error to the console
exit(0); // Exit the application
}

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Unbind our frame buffer
}

void init(void) {
glEnable(GL_TEXTURE_2D); // Enable texturing so we can bind our frame buffer texture
glEnable(GL_DEPTH_TEST); // Enable depth testing

initFrameBuffer(); // Create our frame buffer object
}

void keyOperations (void) {
if (keyStates['a']) { // If the a key has been pressed
// Perform 'a' key operations
}
}

void renderTeapotScene(void) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); // Bind our frame buffer for rendering
glPushAttrib(GL_VIEWPORT_BIT | GL_ENABLE_BIT); // Push our glEnable and glViewport states
glViewport(0, 0, window_width, window_height); // Set the size of the frame buffer view port

glClearColor (0.0f, 0.0f, 1.0f, 1.0f); // Set the clear colour
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear the depth and colour buffers
glLoadIdentity();  // Reset the modelview matrix

glTranslatef(0.0f, 0.0f, -5.0f); // Translate back 5 units

glRotatef(rotation_degree, 1.0f, 1.0f, 0.0f); // Rotate according to our rotation_degree value

glutSolidTeapot(1.0f); // Render a teapot

glPopAttrib(); // Restore our glEnable and glViewport states
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Unbind our texture

rotation_degree += 0.5f;
if (rotation_degree > 360.0f)
rotation_degree = 0.0f;
}

void display (void) {
keyOperations(); // Perform any key presses

renderTeapotScene(); // Render our teapot scene into our frame buffer

glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // Clear the background of our window to red
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Clear the colour buffer (more buffers later on)
glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations

glTranslatef(0.0f, 0.0f, -2.0f);

glBindTexture(GL_TEXTURE_2D, fbo_texture); // Bind our frame buffer texture

glBegin(GL_QUADS);

glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner

glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner

glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner

glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner

glEnd();

glBindTexture(GL_TEXTURE_2D, 0); // Unbind any textures

glutSwapBuffers();
}

void reshape (int width, int height) {
glViewport(0, 0, (GLsizei)width, (GLsizei)height); // Set our viewport to the size of our window
glMatrixMode(GL_PROJECTION); // Switch to the projection matrix so that we can manipulate how our scene is viewed
glLoadIdentity(); // Reset the projection matrix to the identity matrix so that we don't get any artifacts (cleaning up)
gluPerspective(60, (GLfloat)width / (GLfloat)height, 1.0, 100.0); // Set the Field of view angle (in degrees), the aspect ratio of our window, and the new and far planes
glMatrixMode(GL_MODELVIEW); // Switch back to the model view matrix, so that we can start drawing shapes correctly
}

void keyPressed (unsigned char key, int x, int y) {
keyStates[key] = true; // Set the state of the current key to pressed
}

void keyUp (unsigned char key, int x, int y) {
keyStates[key] = false; // Set the state of the current key to not pressed
}

int main (int argc, char **argv) {
glutInit(&argc, argv); // Initialize GLUT
glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); // Set up a basic display buffer (only single buffered for now)
glutInitWindowSize (500, 500); // Set the width and height of the window
glutInitWindowPosition (100, 100); // Set the position of the window
glutCreateWindow ("You’re first OpenGL Window"); // Set the title for the window

if (GLEW_OK != glewInit()) {
std::cout << "Couldn't initialize GLEW" << std::endl;
exit(0);
}

init();

glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering
glutIdleFunc(display); // Tell GLUT to use the method "display" for rendering

glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping

glutKeyboardFunc(keyPressed); // Tell GLUT to use the method "keyPressed" for key presses
glutKeyboardUpFunc(keyUp); // Tell GLUT to use the method "keyUp" for key up events

glutMainLoop(); // Enter GLUT's main loop
}
VN:F [1.9.3_1094]
Please rate so I know where to improve the site. 1 means needs a lot of improvement, 10 means perfect. If you leave a low rating, please state why. I don't want people just coming to bash the site.
Rating: 9.0/10 (11 votes cast)
VN:F [1.9.3_1094]
Rating: +6 (from 6 votes)

3. OpenGL Tips

Posted on : 25-03-2010 | By : Swiftless | In : Miscellaneous, OpenGL

7

On this page, I am going to list a bunch of tips for OpenGL that I gather over time. To start off with this page will be small, but you can expect it to grow eventually :D

If you have any suggestions as to what should be on this page, please email me at swiftless@gmail.com

Cross Platform Compiling

Z Buffer Precision

Lighting Position

Lighting – Point vs Directional

GLEW
Cross Platform Compiling

Having moved from Windows to Linux to Mac. The conversion of most of my code from Windows to Linux was extremely straightforward, just remove all windows dependencies. But when going to Mac, I found the following if statement quite useful, as the header files for OpenGL are located in a different directory.

#if ((defined(__MACH__))&&(defined(__APPLE__)))
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#include <OpenGL/glext.h>
#else
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include <GL/glext.h>
#endif

Z Buffer Precision

The Z buffer is one of the key items overlooked by beginner OpenGL programmers. If you find an application that is using a near plane of something like 0.001 and a far plane similar to 10000. This is going to cause alot of problems along the line.

Because of the way the Z buffer works, it looses depth accuracy for shapes further away. And it can get to a point where the precision is that poor, that two objects can be interpreted as having the same z value. This can cause sheering, and backward objects to be rendered first.

To fix this, it is always best to set your near plane to just before the closest object, and your far plane just past the last object you want to render.

Lighting Position

When placing your lights in the scene, some people make the mistake of placing it during the init method (I have been guilty of this), or calling their lighting position before they place their camera in the world.

They may then run their program, and find that rotating around the scene, and moving, can cause weird artifacts where their shapes become darker, or lighter.

To fix this, you have to remember that when you make the call to set the position of the light, it is multiplied by the current ModelView Matrix. This means that if you call it before you place your camera, the lights are based around the origin (0,0,0) and your objects will actually be moving around them.

You need to first place your camera, and then set your lights, and finally draw all your objects.

This will give you correct lighting, no matter where you have place the camera in the scene.

Lighting – Point vs Directional

One of the snags people get into with lighting, is that they don’t understand the W component of the light position. This value tells OpenGL what type of light we are using. This can have either 1 of 2 values. They are:

0.0 and 1.0

Now the difference between these two, is that a value of 1.0 will tell OpenGL that we want a point light. A point light sits in a particular place in the world, and emits the light in all directions, you can think of it as a light bulb.

A value of 0.0 will give you a directional light, which is similar to an ambient light, as in it affects everything, but it faces a certain direction instead of emitting in all directions.

The point of a point light, is that we can add attenuation values, these determine how far our light affects from the light position.

Now you may ask, what about a spotlight? Well this is simply a point light with some extra attenuation values. It still takes a 1.0 value in the W component, but it takes particular spot light attentuation values, which give it a direction, a range and an angle for the field of view of the light.

A spotlight is best thought of as a torch. It is a point light (it has a bulb), but it has something obscuring it from shining behind, and the casing also tells us how far around we can shine.

GLEW
When using GLEW, there is one crucial thing, which unless you are on OSX, a lot of people overlook, and then wonder why their application fails at runtime.

This is the call to glewInit();

Because OSX’s user interface uses OpenGL, GLEW is already initiated, and therefore a specific call to glewIinit() in your application is redundant. But on a Windows and Linux machine, this call needs to be made *before* any of the GLEW methods are used. Otherwise you will receive an error when you launch your application and your application will fail.

VN:F [1.9.3_1094]
Please rate so I know where to improve the site. 1 means needs a lot of improvement, 10 means perfect. If you leave a low rating, please state why. I don't want people just coming to bash the site.
Rating: 7.6/10 (8 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

1. OpenGL Drivers

Posted on : 25-03-2010 | By : Swiftless | In : Miscellaneous, OpenGL

19

Drivers needed to use OpenGL

Back to OpenGL Tutorial Index

Here I have gathered the OpenGL drivers, the OpenGL library files along with the OpenGL header and include files for compilers. The include files (.h) go into the include folder, and the library files (.lib) go into the library folder. Also the drivers (.dll) will go into your system folder (windows prior to 2000) or in your system32 folder (windows 2000 and greater).

Note: If you are not using Windows Visual C++ 6 then you may have to link the opengl library files to your project manually.

*Keep in mind, not all files here may be up to date, look for more recent version of glut, glew, glext and vbogl.*

 

DLL Files / Drivers

Include: glu32.dll, glut32.dll, glut.dll, opengl32.dll

Download

 

Visual Basic Type Library

vbogl.tlb

Download

 

C++ Include Files

Include: GLU.H, GL.H, GLAUX.H, GL/GL.H, GL/GLAUX.H, GL/glext.h, GL/GLU.H, GL/glu_.h, GL/glut.h

Download

 

C++ Library Files

Include: GLAUX.LIB, OPENGL32.LIB, glut32.lib, glu32.lib

Download

 

All the Files in one Download (C++)

Include: All files mentioned above

Download

VN:F [1.9.3_1094]
Please rate so I know where to improve the site. 1 means needs a lot of improvement, 10 means perfect. If you leave a low rating, please state why. I don't want people just coming to bash the site.
Rating: 9.6/10 (40 votes cast)
VN:F [1.9.3_1094]
Rating: +13 (from 13 votes)

6. Terrain Vertex Buffer Objects

Posted on : 25-03-2010 | By : Swiftless | In : OpenGL, Terrain

Tags: , , , , , , ,

0

Jump To:

heightfield.h Source
heightfield.cpp Source

main.cpp Source
Download

Heightfield.H File:

VBO’s. Well well, this means a total change to our Render function, a new Init function and a few new classes and a few new variables. This is going to be a major change to our code, but in this case, with change comes progress, and wouldn’t you love to go from the current 5 frames per second, to say, 30 or 60? I know I would :-) So lets get to it.

First off, we are going to create a couple of extra classes. The first we are going to call Vert and will hold all our vertices for our terrain, and the second is going to be called TexCoord and guess what?! It will hold all of our texture coordinates for our terrain :-) Lets take a look at the code we need to add:

First our Vert class, which needs to hold the x, y and z coordinates for our terrain vertices:
// class Vert{
// public:
// float x;
// float y;

// float z;
// };

And second we have our TexCoord class which holds the u and v texture coordinates for our terrain:
// class TexCoord{
// public:
// float u;

// float v;
// };

Now I don’t know about you, but basic C++ knowledge explains the above simply enough. I’m going to assume you have at least some idea of what classes are. So lets go take a look at the changes to our SwiftHeightField class, starting off with our new variables. All our variables are going to be private to only our class functions, and will all be used for the creation and use of our Vertex Buffer Objects. So first off we have vhVertexCount. This will hold how many vertices our terrain has and is calculated by the width * height of our heightfield.raw file. Next up is vhVertices, which attached to our new Vert class will hold all our vertices, and finally we have vhTexCoords which used with our TexCoord class will hold all of the texture coordinates we are going to need:

// int vhVertexCount;
// Vert *vhVertices;
// TexCoord *vhTexCoords;

Now lets look at the last two variables we are going to need to add to our heightfield.h file:

// unsigned int vhVBOVertices;
// unsigned int vhVBOTexCoords;

These are going to be the buffers used for the creation of our Vertex Buffer Object. Please see the ‘Vertex Buffer Object’ tutorial (coming soon) for a possibly more indepth tutorial on how these work. As for now, lets look at the new function we are going to add. This is going to be called Init and guess what?! It is going to Initialize our Vertex Buffer Object for us :-)

// bool Init(void);

This will be in the private section of our class and will return true if our program is able to create our Vertex Buffer Object :-)

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.

  #include <windows.h>

class Vert{
    

public:
    
float x;
    
float y;
    
float z;
};

class TexCoord{
    
public:
    
float u;
    
float v;

};

class SwiftHeightField {
    
private:
    
int hmHeight;
    
int hmWidth;
    

    
int vhVertexCount;
    
Vert *vhVertices;
    
TexCoord *vhTexCoords;
    

    
unsigned int vhVBOVertices;
    

unsigned int vhVBOTexCoords;
    

    
unsigned int tID[2];
    

    
bool Init(void);
    

    
public:
    
bool Create(char *hFileName, const int hWidth, const int hHeight);
    

    
void Render(void);
    

    
BYTE hHeightField[1024][1024];

};

Heightfield.CPP File:

Who’s ready for a major code overhaul?! I know I am :-) So without further ado:

Starting off, we are going to add a couple of new header files, these are the glew.h and glext.h files, used for the incorporation of OpenGL extensions within our program. They just let us call the extensions without us having to link them to OpenGL manually. So we have the new lines:

// #include <GL/glew.h>
// #include <GL/glext.h>

Simple enough so far? Good, now lets look at our new Init function. The following code is the outline of our function. Pretty bland isn’t it?

// bool SwiftHeightField::Init(void){

// return true;
// }

Now lets look at generating a Vertex Buffer Object for our vertices :-) First off, we need to call glGenBuffersARB to tell OpenGL that we want to generate a buffer for our Vertex Buffer Object:

// glGenBuffersARB(1, &vhVBOVertices);

Then we need to bind that buffer as an array for OpenGL to fill with data:

// glBindBufferARB(GL_ARRAY_BUFFER_ARB, vhVBOVertices);

And finally we need to fill the Vertex Buffer Object with our vertex information that is loaded in our Create function:

// glBufferDataARB(GL_ARRAY_BUFFER_ARB, vhVertexCount * 3 * sizeof(float), vhVertices, GL_STATIC_DRAW_ARB);

The last line here is telling OpenGL to fill our current open buffer vhVBOVertices, with our vertices vhVertices, the size of our vertex count vhVertexCount, multiplied by 3 (for our x, y and z coordinates), then multiplied by the size of a float variable. We set our Vertex Buffer Object to GL_STATIC_DRAW_ARB, because we don’t plan on updating/changing the vertices inside our Vertex Buffer Object as a terrain is generally static (unless you want to get into destructible terrain).

After we have generated, bound and filled our buffer, we are going to want to just clean up after ourselves and delete all the information in vhVertices, and set it to nothing to save system resources:

// delete [] vhVertices;
// vhVertices = NULL;

Did you get all that? If not, you can email me, but for now lets continue the tutorial :-) After you have done this for your vertices, you are going to want to do the same for our texture coordinates. This is done the exact same way, just changing which variables we are using for what:

// glGenBuffersARB(1, &vhVBOTexCoords);
// glBindBufferARB(GL_ARRAY_BUFFER_ARB, vhVBOTexCoords);
// glBufferDataARB(GL_ARRAY_BUFFER_ARB, vhVertexCount * 2 * sizeof(float), vhTexCoords, GL_STATIC_DRAW_ARB);

Just make sure here, that you change the 3 from the previous code, to a 2. This is because last time with our vertices, we were working with x, y and z floats for our coordinates, now we are only working with u and v floats for our texture coordinates. Vertices have an extra third dimension to work with, while textures only work in two dimensions. And also remember to clean up after with this as well:

// delete [] vhTexCoords;

// vhTexCoords = NULL;

Now let us move onto our Create function. This is where the next major change occurs. Not in the loading of the terrain, but the loading of the terrain data into our vhVertices and vhTexCoords variables for use in our Init function.

The first one we are going to look at is the preperation of our vhVertices and vhTexCoords variables. First off we are going to declare just how many vertices our Vertex Buffer Object will be using. This is done by multiplying the height by the width by 6 of the terrain. We need to multiply by 6 because we are using triangles, are two triangles make up a ‘patch’ or polygon and there are 3 vertices in each triangle. The extra code division of (1 * 1) will be used later on when I add some REALLY basic Level Of Detail optimization to just lower how many polygons are drawn. At this stage, we are drawing every polygon at once, so we divide it by (1 * 1) = 1 to get our initial number of vertices. Next up we set our vhVertices variable to an array the size of the number of vertices that we have. And we do the same for our texture coordinates.

// vhVertexCount = (int)(hmHeight * hHeight * 6) / (1 * 1);
// vhVertices = new Vert[vhVertexCount];
// vhTexCoords = new TexCoord[vhVertexCount];

After this, we have a few temporary variables, these are nIndex, flX and flZ. These are going to hold which vertex we are up to and which vertex we are working on.

// int nIndex = 0;
// float flX;
// float flZ;

Now we are going to enter a couple of loops that will go through our terrain from left to right, and front to back (entirely open to interpretation).

// for (int hMapX = 0; hMapX < hmWidth; hMapX++){

// for (int hMapZ = 0; hMapZ < hmHeight; hMapZ++){

Inside our loops we are going to add another third loop, which will go through each vertex of the current triangle we are up to.

// for (int nTri = 0; nTri < 6; nTri++){

Now while we are looping through everything, here is where we calculate everything we need. First off we are going to temporarily work out which X and Z coordinate we are up to for our vertex. The following two lines of code read (in pseudocode):

if nTri = 1 or 2 or 5{
hMapX = hMapX + 1;
}
else
{
hMapX = hMapX;

}

And the same goes for the Z value we are currently up to, only now using triangles 2, 4 and 5.

// flX = (float)hMapX + ((nTri == 1 || nTri == 2 || nTri == 5) ? 1 : 0);
// flZ = (float)hMapZ + ((nTri == 2 || nTri == 4 || nTri == 5) ? 1 : 0);

These next three lines, are simply setting the x, y and z values of our current vertex in vhVertices to what it would be if we were just plain out using our render code from the previous tutorials, only difference is I have condensed it with the above lines.

// vhVertices[nIndex].x = flX;

// vhVertices[nIndex].y = hHeightField[(int)flX][(int)flZ];
// vhVertices[nIndex].z = flZ;

The next two lines are devoted to setting the texture coordines for our current VhTexCoords just as we did on the fly in our previous tutorials.

// vhTexCoords[nIndex].u = flX / 1024;
// vhTexCoords[nIndex].v = flZ / 1024;

And the final line of nIndex++ just increments which vertex we are actually up to.

// nIndex++;
// }
// }
// }

Now while we are still in our Create function, we need to call our Init function now that we have everything read in and ready to transfer to our Vertex Buffer Objects. To do this, I just call the line:

// Init();

At some stage after all of the above code :-) Now let us move onwards to our Render function!

Now to start off, we are going to just delete EVERYTHING from inside our Render function and start from scratch :-) Hehe. Sounds like alot of fun now don’t it? Of course it is! We LOVE rewriting functions don’t we?! :-D Anyways…. Now that you are prepared, it’s actually not that tricky. Because we are working with Vertex Buffer Objects, we also start working with Client States. These work in exactly the same way (syntax wise) as glEnable and glDisable. Only now we have glEnableClientState and glDisableClientState. So first off we are going to enable our texture coordinate array client state, enable texturing and then bind our texture like so:

// glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// glEnable(GL_TEXTURE_2D);
// glBindTexture(GL_TEXTURE_2D, tID[0]);

Next off, we need to bind which vertex buffer object goes with this texture coordinate client state. This is our vhVBOTexCoords vertex buffer object that we created in our Init function. After we have bound which buffer object we are going to use, we need to call glTexCoordPointer to tell OpenGL that this is a texture coordinate pointer.

// glBindBufferARB(GL_ARRAY_BUFFER_ARB, vhVBOTexCoords);
// glTexCoordPointer(2, GL_FLOAT, 0, (char *) NULL);

The next three lines are similar, only this time we are working with our vertex buffer, and not our texture coordinate buffer. So we first need to enable the client state we are going to be using:

// glEnableClientState(GL_VERTEX_ARRAY);

Then we have to bind our buffer vhVBOVertices and call glVertexPointer. Now that we have everything set up, it is time to draw our Vertex Buffer Object.

// glBindBufferARB(GL_ARRAY_BUFFER_ARB, vhVBOVertices);
// glVertexPointer(3, GL_FLOAT, 0, (char *) NULL);

We do this with the following line. Yes, it is only one line of code to actually draw it, everything else is setting up to draw it :-) We are drawing our vertex arrays as a set of triangles, starting at position 0 in our Vertex Buffer Object and ending at the size of vhVertexCount.

// glDrawArrays(GL_TRIANGLES, 0, vhVertexCount);

Now that we have setup and drawn our Vertex Buffer Object, we need to disable our client states and texturing with the following lines:

// glDisableClientState(GL_VERTEX_ARRAY);

// glDisable(GL_TEXTURE_2D);
// glDisableClientState(GL_TEXTURE_COORD_ARRAY);

Phew, now we can finally move away from this file :-)

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.

  #include <stdio.h>

#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glext.h>

#include “jpeg.h”
#include “heightfield.h”

bool SwiftHeightField::Init(void){
    

glGenBuffersARB(1, &vhVBOVertices);
    
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vhVBOVertices);
    
glBufferDataARB(GL_ARRAY_BUFFER_ARB, vhVertexCount * 3 * sizeof(float), vhVertices, GL_STATIC_DRAW_ARB);

    

    
glGenBuffersARB(1, &vhVBOTexCoords);
    
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vhVBOTexCoords);
    

glBufferDataARB(GL_ARRAY_BUFFER_ARB, vhVertexCount * 2 * sizeof(float), vhTexCoords, GL_STATIC_DRAW_ARB);

    

    
delete [] vhVertices;
    
vhVertices = NULL;
    

    
delete [] vhTexCoords;
    

vhTexCoords = NULL;
    

    
return true;
}

bool SwiftHeightField::Create(char *hFileName, const int hWidth, 
const int hHeight){
    

hmHeight = hHeight;
    
hmWidth = hWidth;
    

    
FILE *fp;
    

    
fp = fopen(hFileName, “rb”);
    

    
fread(hHeightField, 1, hWidth * hHeight, fp);
    

    
fclose(fp);
    

    
vhVertexCount = (int)(hmHeight * hmWidth * 6) / (1 * 1);
    

vhVertices = new Vert[vhVertexCount];
    
vhTexCoords = new TexCoord[vhVertexCount];
    

    
int nIndex = 0;
    
float flX;
    
float flZ;
    

    
for (int hMapX = 0; hMapX < hmWidth; hMapX++){
        

for (int hMapZ = 0; hMapZ < hmHeight; hMapZ++){
            
for (int nTri = 0; nTri < 6; nTri++){
                

flX = (float)hMapX + ((nTri == 1 || nTri == 2 || nTri == 5) ? 1 : 0);
                

flZ = (float)hMapZ + ((nTri == 2 || nTri == 4 || nTri == 5) ? 1 : 0);
                

                
vhVertices[nIndex].x = flX;
                
vhVertices[nIndex].y = hHeightField[(int)flX][(int)flZ];
                

vhVertices[nIndex].z = flZ;
                

                
vhTexCoords[nIndex].u = flX / 1024;
                

vhTexCoords[nIndex].v = flZ / 1024;
                
nIndex++;
            
}
        

}
    
}
    

    
SwiftTextureJpeg(tID, “texture.jpg”, 0);
    

    
Init();
    

    
return true;
}

void SwiftHeightField::Render(void){
    

glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    
glEnable(GL_TEXTURE_2D);
    
glBindTexture(GL_TEXTURE_2D, tID[0]);
    

    
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vhVBOTexCoords);
    
glTexCoordPointer(2, GL_FLOAT, 0, (char *) NULL);
    

    
glEnableClientState(GL_VERTEX_ARRAY);
    
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vhVBOVertices);
    
glVertexPointer(3, GL_FLOAT, 0, (char *) NULL);
    

    
glDrawArrays(GL_TRIANGLES, 0, vhVertexCount);
    

    
glDisableClientState(GL_VERTEX_ARRAY);
    

    

glDisable(GL_TEXTURE_2D);
    
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

Main.CPP File:

Our major changes to this file are going to take place in the form of initializing our extensions that we are going to use. To do this, I am first going to create a function called initExtensions which is where we are going to initialize all our extensions:

// void initExtensions(void){
// }

Now that we have a nice neat place to initialize our extensions, lets initialize them:

// glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
// glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
// glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");

// glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");

The above extensions are everything we need to create and use our Vertex Buffer Objects. As we now have our extensions initialized and ready to use, we need to call the initExtensions function. I am doing this inside our Init function *before* we create our heightfield. This is because the extensions are needed for the creation of our Vertex Buffer Objects, not just in drawing them. So our Init function now looks like:

// void Init (void) {
// glEnable(GL_DEPTH_TEST);
// glDepthFunc(GL_LEQUAL);

// initExtensions();

// hField.Create("heightField.raw", 1024, 1024);
// }

And that is it for now. Check out the next tutorial on optimizing the heightfield using for a good solid 60 frames per second here :-)

If you have any questions, just 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.

  #include <GL/glew.h>

#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/GLUT.h>

#include <math.h>

#include <windows.h>
#include <stdio.h>

#include <string.h>
#include <fstream>
#include <assert.h>

#include “heightfield.h”

#pragma comment(lib,“glew32.lib”)

float xpos = 851.078, ypos = 351.594, zpos = 281.033, xrot = 758, yrot = 238,
 angle=0.0;

float lastx, lasty;

float bounce;
float cScale = 1.0;

SwiftHeightField hField;

void camera (void) {
    
int posX = (int)xpos;
    

int posZ = (int)zpos;
    

    
glRotatef(xrot,1.0,0.0,0.0);
    
glRotatef(yrot,0.0,1.0,0.0);
    

glTranslated(-xpos,-ypos,-zpos);
}

void display (void) {
    

glClearColor (0.0,0.0,0.0,1.0);
    
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    

glLoadIdentity();
    
camera();
    

    
glPushMatrix();
    
hField.Render();
    

glPopMatrix();
    

    
glutSwapBuffers();
}

void initExtensions(void){
    

glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress(“glGenBuffersARB”);
    
glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress(“glBindBufferARB”);

    
glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress(“glBufferDataARB”);
    
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress(“glDeleteBuffersARB”);

}

void Init (void) {
    
glEnable(GL_DEPTH_TEST);
    

glDepthFunc(GL_LEQUAL);
    

    
initExtensions();
    

    
hField.Create(“heightField.raw”, 1024, 1024);

}

void mouseMovement(int x, int y) {
    
int diffx=x-lastx;
    

int diffy=y-lasty;
    
lastx=x;
    
lasty=y;
    
xrot += (float) diffy;
    

yrot += (float) diffx;
}

void keyboard (unsigned char key, int x, int y) {
    

    
if (key == ‘w’)
    
{
        
float xrotrad, yrotrad;
        

yrotrad = (yrot / 180 * 3.141592654f);
        
xrotrad = (xrot / 180 * 3.141592654f);
        

xpos += float(sin(yrotrad)) * cScale;
        
zpos -= float(cos(yrotrad)) * cScale;
        

ypos -= float(sin(xrotrad)) ;
        
bounce += 0.04;
    

}
    

    
if (key == ‘s’)
    
{
        
float xrotrad, yrotrad;
        

yrotrad = (yrot / 180 * 3.141592654f);
        
xrotrad = (xrot / 180 * 3.141592654f);
        

xpos -= float(sin(yrotrad)) * cScale;
        
zpos += float(cos(yrotrad)) * cScale;
        

ypos += float(sin(xrotrad));
        
bounce += 0.04;
    

}
    

    
if (key == ‘d’)
    
{
        
float yrotrad;
        

yrotrad = (yrot / 180 * 3.141592654f);
        
xpos += float(cos(yrotrad)) * cScale;
        

zpos += float(sin(yrotrad)) * cScale;
    
}
    

    
if (key == ‘a’)
    
{
        
float yrotrad;
        

yrotrad = (yrot / 180 * 3.141592654f);
        
xpos -= float(cos(yrotrad)) * cScale;
        

zpos -= float(sin(yrotrad)) * cScale;
    
}
    

}

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, 1000.0);
    

glMatrixMode (GL_MODELVIEW);
}

int main (int argc, char **argv) {
    

glutInit(&argc, argv);
    
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA);
    

glutInitWindowSize(500, 500);
    
glutInitWindowPosition(100, 100);
    
glutCreateWindow(“A basic OpenGL Window);
    

Init();
    
glutDisplayFunc(display);
    
glutIdleFunc(display);
    

glutReshapeFunc(reshape);
    
glutKeyboardFunc(keyboard);
    
glutPassiveMotionFunc(mouseMovement);
    

glutMainLoop ();
    
return 0;
}

Download:

Download heightfield.h Source Code for this Tutorial

Download heightfield.cpp Source Code for this Tutorial

Download main.cpp Source Code for this Tutorial

Download heightfield.raw for this Tutorial

Download texture.jpg for this Tutorial

Download jpeg.h Source Code for this Tutorial

Download jpeg.cpp Source Code for this Tutorial

Download jpeglib.zip for this Tutorial

VN:F [1.9.3_1094]
Please rate so I know where to improve the site. 1 means needs a lot of improvement, 10 means perfect. If you leave a low rating, please state why. I don't want people just coming to bash the site.
Rating: 7.0/10 (4 votes cast)
VN:F [1.9.3_1094]
Rating: +3 (from 3 votes)

35. OpenGL Tiling Engine

Posted on : 25-03-2010 | By : Swiftless | In : OpenGL

Tags: , , , ,

12

Well due to a request, here is a tiling example for you all.

To tile in OpenGL, it is not that easy to merge or combine textures to create a simple 2D tiling game.
It is easier for us to draw a bunch of quads that take up the space of the world in which we want
to tile and assign different textures to those quads according to what is in our map file.

Here is the example of a map file that I am using for this tutorial, which is integrated into the code:

int cMap[10][10] = { //our map
    {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
    {1, 0, 1, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 1, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 1, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 1, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 1, 1, 1, 1, 1, 1, 1, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 1},
    {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
};

Now if you don’t like storing this in your program, you can just as easily store all of this in a file and loadit in when you need it.

So now that we have our ‘map’, we want to load in our two textures for our two states, 1 or 0, which will beeither grass or dirt in the case of the textures I have created.

I am setting 1 to be dirt, and 0 will be grass.

So as this is building on from the texturing tutorial, we need to create another variable to hold our other texture, as we already setup the first texture in the first tutorial.
GLuint texture2; //the array for our second texture

Now that we have our textures, we need to load them in, do this just like you did in the texturing tutorial:

    //Load our texture
    texture = LoadTexture(“texture.raw”, 256, 256);
    texture2 = LoadTexture(“texture2.raw”, 256, 256);

    glutMainLoop ();

    //Free our texture
    FreeTexture(texture);
    FreeTexture(texture2);

So now that we have our map, and our textures, we need to now draw it all. I am creating a method called drawTiles(). This method will be called in the display function and takes no parameters.

void drawTiles (void) { //our function to draw the tiles
First thing we want to do is start looping through the height of the map, the value of 10 here is specific
to this application. If you have a bigger or smaller map, change this value as you need.
for (int i = 0; i < 10; i++) //loop through the height of the map
{
Then we will want to loop through the width of the map, just as I said above, the value 10 is specific
to this tutorial.
for (int j = 0; j < 10; j++) //loop through the width of the map
{
Now here is where the main part of our tiling takes place, we need to check if the map value at the
current [i][j] position is either a 0 or a 1. If it is a 0, then we bind the grass texture to the quad
that we are about to draw.
if (cMap[i][j] == 0) //if the map at this position contains a 0
{
glBindTexture( GL_TEXTURE_2D, texture ); //bind our grass texture to our shape
}
Else we automatically assign it to dirt. If you have more than two states, you will need to make several if statements,
and if you do not want a default, then you will remove the else and replace it with an if.
else //otherwise
{
glBindTexture( GL_TEXTURE_2D, texture2 ); //bind our dirt texture to our shape
}

The rest of the code draws a quad at the current i, j position we are at.
Drawing a quad should be simple for you at this stage. So I will not explain it.
glPushMatrix(); //push the matrix so that our translations only affect this tile
glTranslatef(j, -i, 0); //translate the tile to where it should belong

glBegin (GL_QUADS); //begin drawing our quads
glTexCoord2d(0.0, 0.0);
glVertex3f(0.0, 0.0, 0.0); //with our vertices we have to assign a texcoord

glTexCoord2d(1.0, 0.0);
glVertex3f(1.0, 0.0, 0.0); //so that our texture has some points to draw to

glTexCoord2d(1.0, 1.0);
glVertex3f(1.0, 1.0, 0.0);

glTexCoord2d(0.0, 1.0);
glVertex3f(0.0, 1.0, 0.0);
glEnd();
glPopMatrix(); //pop the matrix
} //end first loop
} //end second loop
}

And there we have it, you can now create a 2D tiled map for use with a game. Simple wasn’t it? :-)
If you have any questions, 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.

  

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

GLuint texture; //the array for our texture
GLuint texture2; //the array for our second texture

int cMap[10][10] = { //our map
    {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
    {1, 0, 1, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 1, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 1, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 1, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 1, 1, 1, 1, 1, 1, 1, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 1},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 1},
    {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
};

//function to load the RAW file

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

    //The following code will read in our RAW 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 );
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, 
GL_MODULATE
 ); //set texture environment parameters

    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 
GL_LINEAR
 );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR
 );

    //Here we are setting the parameter to repeat the texture 
instead of clamping the texture

    //to the edge of our shape. 
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 
GL_REPEAT
 );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 
GL_REPEAT
 );

    //Generate the texture
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, 
GL_RGB,
 GL_UNSIGNED_BYTE, data);
    free( data ); //free the texture
    return texture; //return whether it was successfull
}

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

void drawTiles (void) { //our function to draw the tiles
    for (int i = 0; i < 10; i++) //loop through the height of the map
    {
        for (int j = 0; j < 10; j++) //loop through the width of the map
        {
            if (cMap[i][j] == 0) //if the map at this position contains a 0
            {
                glBindTexture( GL_TEXTURE_2D, texture ); //bind our grass texture to our shape
            }
            else //otherwise
            {
                glBindTexture( GL_TEXTURE_2D, texture2 ); //bind our dirt texture to our shape
            }
            
            glPushMatrix(); //push the matrix so that our translations only affect
 this tile

                glTranslatef(j, -i, 0); //translate the tile to where it should belong

                glBegin (GL_QUADS); //begin drawing our quads
                    glTexCoord2d(0.0, 0.0);
                    glVertex3f(0.0, 0.0, 0.0); //with our vertices we have to assign a texcoord

                    glTexCoord2d(1.0, 0.0);
                    glVertex3f(1.0, 0.0, 0.0); //so that our texture has some points to draw to

                    glTexCoord2d(1.0, 1.0);
                    glVertex3f(1.0, 1.0, 0.0);

                    glTexCoord2d(0.0, 1.0);
                    glVertex3f(0.0, 1.0, 0.0);
                glEnd();
            glPopMatrix(); //pop the matrix
        } //end first loop
    } //end second loop
}

void display (void) {
    glClearColor (0.0,0.0,0.0,1.0);
    glClear (GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    glEnable( GL_TEXTURE_2D );
    glTranslatef(-5, 4, -20); //translate back a bit to view the map correctly

    drawTiles(); //draw our tiles
    glutSwapBuffers();
}
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);
    glutInitWindowSize (500, 500);
    glutInitWindowPosition (100, 100);
    glutCreateWindow (“A basic OpenGL Window);
    glutDisplayFunc (display);
    glutIdleFunc (display);
    glutReshapeFunc (reshape);

    //Load our texture
    texture = LoadTexture(“texture.raw”, 256, 256);
    texture2 = LoadTexture(“texture2.raw”, 256, 256);

    glutMainLoop ();

    //Free our texture
    FreeTexture(texture);
    FreeTexture(texture2);

    return 0;
}

Download Texture(.RAW file)

Download the second Texture (.RAW file)

VN:F [1.9.3_1094]
Please rate so I know where to improve the site. 1 means needs a lot of improvement, 10 means perfect. If you leave a low rating, please state why. I don't want people just coming to bash the site.
Rating: 8.7/10 (3 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

34. Orthogonal Projections

Posted on : 25-03-2010 | By : Swiftless | In : OpenGL

Tags: , , , , ,

3

Welcome to another in my slowly building long line of OpenGL tutorials.
Here we will be looking at how to implement orthogonal projections to
incorporate 2D into our program. This will draw our shape in a space that
will not move when our 3D scene is rotated. This is perfect when
implementing a display system into your game that displays health, ammo
etc. So lets get started now.

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

The only global variables I am going to be using in this are:
int w1;
int h1;

These will simply hold our window width and height as gathered in our
reshape function.

————————————————————————-
Section 2: Starting the orthogonal projection.

This call, no matter when it is called, will start our orthogonal
projection, I have called it orthogonalStart:

void orthogonalStart (void) {
First of all, we need to switch to our projection matrix
glMatrixMode(GL_PROJECTION);
Start our projection modifications
glPushMatrix();
Then we need to clear it of all previous information
glLoadIdentity();
Now I am calling: gluOrtho2D instead of the previous gluPerspective
which was in our reshape function. This takes our parameters which
set the view space from 0,0 in the window, to the width and height of the
window which we collect in our reshape function
gluOrtho2D(0, w1, 0, h1);
Now we need to flip our scene upside down
glScalef(1, -1, 1);
And translate it to display our scene correctly
glTranslatef(0, -h1, 0);
Now we switch back to our model matrix so we can draw our 2D shapes
glMatrixMode(GL_MODELVIEW);
}

———————————————————————
Section 3: Ending the orthogonal projection and restoring our perspective
projection

To end our orthogonal projection, I am calling the function, which I have
called orthogonalEnd which will set our scene back to how it was
void orthogonalEnd (void) {
Switch back to our projection mode
glMatrixMode(GL_PROJECTION);
Finish our calls above
glPopMatrix();
Switch back to our model matrix to continue with out 3D scene
glMatrixMode(GL_MODELVIEW);
}

———————————————————————
Section 4: Display

Now to draw our information, I have called the following from within
my display function:

Call our function to start our orthogonal projections
orthogonalStart();

Begin drawing a quad, note here that in orthogonal space, we work with
pixels as opposed to units in 3D space which have no length of measurement
glBegin(GL_QUADS);
Now I am drawing our vertices in 2D as opposed to 3D vertices which
are called with glVertex3f. The following coordinates will draw a square right
in the middle of our 500×500 window.
glVertex2f(125, 125);
glVertex2f(125, 375);
glVertex2f(375, 375);
glVertex2f(375, 125);
glEnd();

Now we call our function to end our orthogonal projections
orthogonalEnd();

——————————————————————–
Section 5: Reshape

In our reshape function I am using the following lines:
w1 = w;
h1 = h;

Which I have called after our gluPerspective call. This will set
our variables to the width and the height of our window.

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

And there we have it, if you have any questions, please 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.
  #include <GL/gl.h>
#include <GL/glut.h>
#include <windows.h>
#include <stdio.h>

int w1;
int h1;

void orthogonalStart (void) {
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(0, w1, 0, h1);
    glScalef(1, -1, 1);
    glTranslatef(0, -h1, 0);
    glMatrixMode(GL_MODELVIEW);
}

void orthogonalEnd (void) {
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
}

void display (void) {
    glClearColor (1.0,0.0,0.0,1.0);
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    orthogonalStart();

    glBegin(GL_QUADS);
    glVertex2f(125, 125);
    glVertex2f(125, 375);
    glVertex2f(375, 375);
    glVertex2f(375, 125);
    glEnd();

    orthogonalEnd();

    glutSwapBuffers();
}

void reshape (int w, int h) {
    glViewport (0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective (60, (GLfloat)w / (GLfloat)h, 0.1, 1000.0);
    w1 = w;
    h1 = h;
    glMatrixMode (GL_MODELVIEW);
}

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

VN:F [1.9.3_1094]
Please rate so I know where to improve the site. 1 means needs a lot of improvement, 10 means perfect. If you leave a low rating, please state why. I don't want people just coming to bash the site.
Rating: 9.5/10 (4 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

33. OpenGL Animating Textures

Posted on : 25-03-2010 | By : Swiftless | In : OpenGL

Tags: , , , ,

2

Some of you might be wondering about animating your textures. Well I have
some bad news for you. OpenGL does not support texture animation. So you
will have no luck loading in a Gif or Movie file into your application and
rendering it straight to your shape. But I also have some good news. There are
ways around this. And I am going to show you the way that I know.

Now my way consists of creating a different image file for every frame in your
animation. If you have a gif file, you can probably find an exporter through a
quick search on the internet to render out every frame from your animation.

So why don’t we get started.

I am only adding one extra variable which will control which frame we are
currently displaying for our animation. You will probably need another
variable for each animation you wish you use. And this variable is simple
enough to call. I am just using:
double frame;

Now we don’t have to set our frame to 0 at the start of our application
because if we do not set a value, it will default to 0.

So far so good?

After that I am then loading every frame when the application loads as a
new part of the texture array. There are 9 files in total, so I am loading
them as 0-8 in my array.
texture[0] = LoadTexture( “textures/1.raw”, 256, 256 );
texture[1] = LoadTexture( “textures/2.raw”, 256, 256 );
texture[2] = LoadTexture( “textures/3.raw”, 256, 256 );
texture[3] = LoadTexture( “textures/4.raw”, 256, 256 );
texture[4] = LoadTexture( “textures/5.raw”, 256, 256 );
texture[5] = LoadTexture( “textures/6.raw”, 256, 256 );
texture[6] = LoadTexture( “textures/7.raw”, 256, 256 );
texture[7] = LoadTexture( “textures/8.raw”, 256, 256 );
texture[8] = LoadTexture( “textures/9.raw”, 256, 256 );

Now if we go to our display function, this is where we are going to set the
texture to our quad. I am simply calling our Texture array as the texture, with
the array number being which frame of the animation we are on. The (int) is to
set our frame to an integer.
glBindTexture( GL_TEXTURE_2D, texture[(int)frame] );

Then I am going to add at the bottom of our display function, the code
that will update which frame we are currently on for the current animation.

So our function will look like:
Here we are increasing which frame of the animation we are on each pass of the
display function. Changing this number will change the speed of the animation.
frame+=0.2;
Now I am checking to see if the frame is above the maximum, and if it is,
change it back to the first frame for a steady loop.
if (frame > 8)
{
frame = 1;
}

And if you run this you will have an animation of a man whose arms spin around.

Well, have fun, but keep in mind that this can be time consuming. And I don’t
really see a use for it at this point in time.

If you have any questions, 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.
  // Some of you might be wondering about animating your textures.
 Well I have

// some bad news for you. OpenGL does not support texture 
animation. So you

// will have no luck loading in a Gif or Movie file into your 
application and

// rendering it straight to your shape. But I also have some 
good news. There are

// ways around this. And I am going to show you the way that I 
know.

// Now my way consists of creating a different image file for 
every frame in your

// animation. If you have a gif file, you can probably find an 
exporter through a

// quick search on the internet to render out every frame from
 your animation.

// So why don’t we get started.

// I am only adding one extra variable which will control which
 frame we are

// currently displaying for our animation. You will probably
 need another

// variable for each animation you wish you use. And this 
variable is simple

// enough to call. I am just using:
// double frame;

// Now we don’t have to set our frame to 0 at the start of our 
application

// because if we do not set a value, it will default to 0.

// So far so good?

// After that I am then loading every frame when the application
 loads as a

// new part of the texture array. There are 9 files in total, so
 I am loading

// them as 0-8 in my array.
//    texture[0] = LoadTextureRAW( “textures/1.raw”, 256, 
256 );

//    texture[1] = LoadTextureRAW( “textures/2.raw”, 256, 
256 );

//    texture[2] = LoadTextureRAW( “textures/3.raw”, 256, 
256 );

//    texture[3] = LoadTextureRAW( “textures/4.raw”, 256, 
256 );

//    texture[4] = LoadTextureRAW( “textures/5.raw”, 256, 
256 );

//    texture[5] = LoadTextureRAW( “textures/6.raw”, 256, 
256 );

//    texture[6] = LoadTextureRAW( “textures/7.raw”, 256, 
256 );

//    texture[7] = LoadTextureRAW( “textures/8.raw”, 256, 
256 );

//    texture[8] = LoadTextureRAW( “textures/9.raw”, 256, 
256 );

// Now if we go to our display function, this is where we are 
going to set the

// texture to our quad. I am simply calling our Texture array 
as the texture, with

// the array number being which frame of the animation we are 
on. The (int) is to

// set our frame to an integer.
// glBindTexture( GL_TEXTURE_2D, texture[(int)frame] )
;

// Then I am going to add at the bottom of our display function,
 the code

// that will update which frame we are currently on for the 
current animation.

// So our function will look like:
// Here we are increasing which frame of the animation we are 
on each pass of the

// display function. Changing this number will change the 
speed of the animation.

//     frame+=0.2;
// Now I am checking to see if the frame is above the maximum, 
and if it is,

// change it back to the first frame for a steady loop.
//     if (frame > 8)
//        {
//     frame = 1;
//     }

// And if you run this you will have an animation of a man whose 
arms spin around.

// Well, have fun, but keep in mind that this can be time consuming.
 And I don’t

// really see a use for it at this point in time.

// If you have any questions, email me at swiftless@gmail.com.

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

GLuint texture[8];

double frame;

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

    //The following code will read in our RAW 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 ); //generate the texture with 
the loaded data

    glBindTexture( GL_TEXTURE_2D, texture ); //bind the texture
 to it’s array

    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, 
GL_MODULATE ); //set texture environment parameters

    //here we are setting what textures to use and when. The MIN 
filter is which quality to show

    //when the texture is near the view, and the MAG filter is which
 quality to show when the texture

    //is far from the view.

    //The qualities are (in order from worst to best)
    //GL_NEAREST
    //GL_LINEAR
    //GL_LINEAR_MIPMAP_NEAREST
    //GL_LINEAR_MIPMAP_LINEAR

    //And if you go and use extensions, you can use Anisotropic 
filtering textures which are of an

    //even better quality, but this will do for now.
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
 GL_LINEAR_MIPMAP_LINEAR );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
 GL_LINEAR_MIPMAP_LINEAR );

    //Here we are setting the parameter to repeat the texture 
instead of clamping the texture

    //to the edge of our shape. 
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 
GL_REPEAT );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 
GL_REPEAT );

    //Generate the texture with mipmaps
    gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height, 
GL_RGB, GL_UNSIGNED_BYTE, data );
    free( data ); //free the texture
    return texture; //return whether it was successfull
}

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

void cube (void) {
    glBindTexture( GL_TEXTURE_2D, texture[(int)frame] );
    glScalef(2,2,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();
}

void display (void) {
    glClearColor (0.0,0.0,0.0,1.0);
    glClear (GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    glEnable( GL_TEXTURE_2D );
    glTranslatef(0,0,-5);
    cube();
    glutSwapBuffers();
    frame+=0.2;
    if (frame > 8)
    {
    frame = 1;
    }
}
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);
}

void init (void) {
    texture[0] = LoadTexture( “textures/1.raw“, 256, 256 );
    texture[1] = LoadTexture( “textures/2.raw“, 256, 256 );
    texture[2] = LoadTexture( “textures/3.raw“, 256, 256 );
    texture[3] = LoadTexture( “textures/4.raw“, 256, 256 );
    texture[4] = LoadTexture( “textures/5.raw“, 256, 256 );
    texture[5] = LoadTexture( “textures/6.raw“, 256, 256 );
    texture[6] = LoadTexture( “textures/7.raw“, 256, 256 );
    texture[7] = LoadTexture( “textures/8.raw“, 256, 256 );
    texture[8] = LoadTexture( “textures/9.raw“, 256, 256 );
}

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

Download Textures(.RAW files in a Zip file)

Download Textures(.BMP files in a Zip file)

VN:F [1.9.3_1094]
Please rate so I know where to improve the site. 1 means needs a lot of improvement, 10 means perfect. If you leave a low rating, please state why. I don't want people just coming to bash the site.
Rating: 8.5/10 (2 votes cast)
VN:F [1.9.3_1094]
Rating: +1 (from 1 vote)

32. OpenGL Particle Engine

Posted on : 25-03-2010 | By : Swiftless | In : OpenGL

Tags: , , , ,

7

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 :-P
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 :P
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)

VN:F [1.9.3_1094]
Please rate so I know where to improve the site. 1 means needs a lot of improvement, 10 means perfect. If you leave a low rating, please state why. I don't want people just coming to bash the site.
Rating: 6.5/10 (4 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 2 votes)

31. OpenGL Sphere Creation

Posted on : 25-03-2010 | By : Swiftless | In : OpenGL

Tags: , , , ,

4

Fancy… Here we are going to create our own sphere without the use of
GLUT. I chose to write this tutorial, because I did not like the way
you could and could not texture the sphere created by GLUT and when
I looked on the internet, I could not find any tutorials on it. The advantage
of our sphere, is that we can set texture coordinates and if need be, place it
inside either a display list, vertex array, or vertex buffer object, allowing
us to call alot more than in immediate mode.

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

Now to start off, here are the variables we are going to be using:

This will hold our texture
GLuint texture[1];

This will just hold our angle of rotation
double angle = 0;

This here will hold the information of all our vertices, such as
x, y and z coordinates along with texture coordinates.
typedef struct
{

The x position of our current vertex
int X;
The Y position of our current vertex
int Y;
The Z position of our current vertex
int Z;

The U(x) texture coordinate of the current vertex
double U;
The V(x) texture coordinate of the current vertex
double V;
}VERTICES;

Next we are going to use PI to convert our angles in degrees to radians.
const double PI = 3.1415926535897;

This here is going to determine how far apart each of our vertices are
the further apart, the faster the program, but the squarer the sphere.
I chose 10 because it looks nice, and runs excellent.
const space = 10;

This will hold out total amount of vertices.
const VertexCount = (90 / space) * (360 / space) * 4;

Now we set up how many vertices we are going to use.
VERTICES VERTEX[VertexCount];

———————————————————————
Section 2: Enabling and Creating

Here I am enabling depth testing, texturing and face culling. The depth testing
is so that we acctually have depth to our scene, the texturing is so that
we can apply textures to our sphere, and culling to used to speed up the
application. I have set the front face for culling to Counter Clock Wise, as triangle
strips cull the opposite face to most other shapes.
glEnable(GL_DEPTH_TEST);
glEnable( GL_TEXTURE_2D );
glDepthFunc(GL_LEQUAL);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);

Now we load our texture to be used. It will be stored in texture[0]
texture[0] = LoadTextureRAW( “texture.raw” );

Now we call to create the sphere, I do not know why it does this, but the first
input seems to choose how many subdivisions to perform. The next lets you choose
where to move the sphere on the x, y and z axis.
CreateSphere(70,0,0,0);

Now for the actual creation code. We are inputting R as the number of subdivisions,
H as the translation on the horizontal axis, K as the translation on the vertical
axis, and Z as the translation on the Z axis.
void CreateSphere (double R, double H, double K, double Z) {
Now are variables for this is as followed. n is the current vertex we are working
with. While a and b are used to control our loops.
int n;
double a;
double b;

Set n to 0 to start off with the first vertex
n = 0;

Assign our b loop to go through 90 degrees in intervals of our variable space
for( b = 0; b <= 90 – space; b+=space){
Assign our a loop to go through 360 degrees in intervals of our variable space
for( a = 0; a <= 360 – space; a+=space){

Start editing our vertex.
I am calculating the X value here.
VERTEX[n].X = R * sin((a) / 180 * PI) * sin((b) / 180 * PI) – H;
The Y value here.
VERTEX[n].Y = R * cos((a) / 180 * PI) * sin((b) / 180 * PI) + K;
The Z value here.
VERTEX[n].Z = R * cos((b) / 180 * PI) – Z;
Now I am calculating the texture coordinates. I have used (2*b) as the texture
is twice as wide as it is high. Hence 2:1. You can remove the (2*) if you wish
to use a texture with the same width and height, or increase it accordingly.
VERTEX[n].V = (2 * b) / 360;
VERTEX[n].U = (a) / 360;

Then start working with the next vertex
n++;

Then we do the same calculations as before, only adding the space variable
to the b values.
VERTEX[n].X = R * sin((a) / 180 * PI) * sin((b + space) / 180 * PI) – H;
VERTEX[n].Y = R * cos((a) / 180 * PI) * sin((b + space) / 180 * PI) + K;
VERTEX[n].Z = R * cos((b + space) / 180 * PI) – Z;
VERTEX[n].V = (2 * (b + space)) / 360;
VERTEX[n].U = (a) / 360;
n++;

Then we do the same calculations as the first, only adding the space variable
to the a values.
VERTEX[n].X = R * sin((a + space) / 180 * PI) * sin((b) / 180 * PI) – H;
VERTEX[n].Y = R * cos((a + space) / 180 * PI) * sin((b) / 180 * PI) + K;
VERTEX[n].Z = R * cos((b) / 180 * PI) – Z;
VERTEX[n].V = (2 * b) / 360;
VERTEX[n].U = (a + space) / 360;
n++;

Then we do the same calculations as the first again, only adding the space variable
to both the b and the a values.
VERTEX[n].X = R * sin((a + space) / 180 * PI) * sin((b + space) / 180 * PI) – H;
VERTEX[n].Y = R * cos((a + space) / 180 * PI) * sin((b + space) / 180 * PI) + K;
VERTEX[n].Z = R * cos((b + space) / 180 * PI) – Z;
VERTEX[n].V = (2 * (b + space)) / 360;
VERTEX[n].U = (a + space) / 360;
n++;

}
}
}

———————————————————————————
Section 3: Displaying the Sphere

To display the sphere I am calling this funtion which will set the size of it
to 5 and assign the texture specified.
DisplaySphere(5, texture[0]);

This is the actual displaying code itself, it inputs the radius and texture
void DisplaySphere (double R, GLuint texture){
This variable will control which vertex we are currently working with
int b;

I have chosen to scale it here to 0.0125 times its original size, and then
increase it by R as the original sphere is rather large.
glScalef (0.0125 * R, 0.0125 * R, 0.0125 * R);

Now I am rotating it because, if you run it, it is sideways :)
glRotatef (90, 1, 0, 0);

Now I bind the texure we inputted above.
glBindTexture (GL_TEXTURE_2D, texture);

Now to begin drawing the sphere itself. I am drawing it with triangle strips
as they are the fastest shape for this.
glBegin (GL_TRIANGLE_STRIP);
Now I am looping through each vertex
for ( b = 0; b <= VertexCount; b++){
Assigning the texture coordinates of the current vertex
glTexCoord2f (VERTEX[b].U, VERTEX[b].V);
And the drawing the specified vertex with the Z coordinate inverted. Because
our creation code only draws half a sphere, which is why I am also doing
this loop again for the other half below.
glVertex3f (VERTEX[b].X, VERTEX[b].Y, -VERTEX[b].Z);
}

And here I do the same as above, only this time, I invert
the V(y) texture coordinate.
for ( b = 0; b <= VertexCount; b++){
glTexCoord2f (VERTEX[b].U, -VERTEX[b].V);
glVertex3f (VERTEX[b].X, VERTEX[b].Y, VERTEX[b].Z);
}

Then end the shape.
glEnd();
}

———————————————————————————
And there we have it. A textured sphere without the need for GLUT

If you have any questions, please 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.

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

GLuint texture[1];

double angle = 0;

typedef struct 
{
    
int X;
    
int Y;
    
int Z;
    

    
double U;
    
double V;
}VERTICES;

const double PI = 3.1415926535897;

const space = 10;

const VertexCount = (90 / space) * (360 / space) * 4;

VERTICES VERTEX[VertexCount];

GLuint LoadTextureRAW( const char * filename );

void DisplaySphere (double R, GLuint texture){
    
int b;
    

    
glScalef (0.0125 * R, 0.0125 * R, 0.0125 * R);
    
    
    
glRotatef (90, 1, 0, 0);
    

    
glBindTexture (GL_TEXTURE_2D, texture);
    

    
glBegin (GL_TRIANGLE_STRIP);
    
for ( b = 0; b <= VertexCount; b++){
        
glTexCoord2f (VERTEX[b].U, VERTEX[b].V);
        
glVertex3f (VERTEX[b].X, VERTEX[b].Y, -VERTEX[b].Z);
    
}
    

    
for ( b = 0; b <= VertexCount; b++){
        
glTexCoord2f (VERTEX[b].U, -VERTEX[b].V);
        
glVertex3f (VERTEX[b].X, VERTEX[b].Y, VERTEX[b].Z);
    
}
    
glEnd();
}

void CreateSphere (double R, double H, double K, double Z) {
    
int n;
    
double a;
    
double b;
    

    
n = 0;
    

    
for( b = 0; b <= 90 - space; b+=space){
        
    for( a = 0; a <= 360 - space; a+=space){
            

            
VERTEX[n].X = R * sin((a) / 180 * PI) * sin((b) / 180 * PI) - H;
            
VERTEX[n].Y = R * cos((a) / 180 * PI) * sin((b) / 180 * PI) + K;
            
VERTEX[n].Z = R * cos((b) / 180 * PI) - Z;
            
VERTEX[n].V = (2 * b) / 360;
            
VERTEX[n].U = (a) / 360;
            
n++;
            

            
VERTEX[n].X = R * sin((a) / 180 * PI) * sin((b + space) / 180 * PI
            
) - H;
            
VERTEX[n].Y = R * cos((a) / 180 * PI) * sin((b + space) / 180 * PI
            
) + K;
            
VERTEX[n].Z = R * cos((b + space) / 180 * PI) - Z;
            
VERTEX[n].V = (2 * (b + space)) / 360;
            
VERTEX[n].U = (a) / 360;
            
n++;
            

            
VERTEX[n].X = R * sin((a + space) / 180 * PI) * sin((b) / 180 * PI
            
) - H;
            
VERTEX[n].Y = R * cos((a + space) / 180 * PI) * sin((b) / 180 * PI
            
) + K;
            
VERTEX[n].Z = R * cos((b) / 180 * PI) - Z;
            
VERTEX[n].V = (2 * b) / 360;
            
VERTEX[n].U = (a + space) / 360;
            
n++;
            

            
VERTEX[n].X = R * sin((a + space) / 180 * PI) * sin((b + space) /
            
180 * PI) - H;
            
VERTEX[n].Y = R * cos((a + space) / 180 * PI) * sin((b + space) /
            
180 * PI) + K;
            
VERTEX[n].Z = R * cos((b + space) / 180 * PI) - Z;
            
VERTEX[n].V = (2 * (b + space)) / 360;
            
VERTEX[n].U = (a + space) / 360;
            
n++;
            

            
    }
    
}
}

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);
    

    
    glRotatef(angle,0,1,0);
    
    DisplaySphere(5, texture[0]);
    

    
    glutSwapBuffers();
    
    angle ++;
}

void init (void) {
    
glEnable(GL_DEPTH_TEST);
    
glEnable( GL_TEXTURE_2D );
    
glDepthFunc(GL_LEQUAL);
    
glCullFace(GL_BACK);
    
glFrontFace(GL_CCW);
    
glEnable(GL_CULL_FACE);
    
texture[0] = LoadTextureRAW( “earth.raw” );
    
CreateSphere(70,0,0,0);
}
void reshape (int w, int h) {
    
    glViewport (0, 0, (GLsizei)w, (GLsizei)h);
    
    glMatrixMode (GL_PROJECTION);
    
    glLoadIdentity ();
    
    gluPerspective (60, (GLfloat)w / (GLfloat)h, 0.1, 100.0);
    
    glMatrixMode (GL_MODELVIEW);
}

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

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

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

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

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

    
  glGenTextures( 1, &texture );
    

    
  glBindTexture( GL_TEXTURE_2D, texture );
    

    
  glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, 
    
GL_MODULATE );
    

    
  glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
    
 GL_LINEAR_MIPMAP_NEAREST );
    

    
  glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
    
 GL_LINEAR );
    

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

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

    
  free( data );
    

    
  return texture;
    

}

Download Sphere Texture(.RAW file)

Download Sphere Texture(.BMP file)

VN:F [1.9.3_1094]
Please rate so I know where to improve the site. 1 means needs a lot of improvement, 10 means perfect. If you leave a low rating, please state why. I don't want people just coming to bash the site.
Rating: 9.8/10 (8 votes cast)
VN:F [1.9.3_1094]
Rating: +2 (from 2 votes)

30. OpenGL Circle Drawing

Posted on : 25-03-2010 | By : Swiftless | In : OpenGL

Tags: , , ,

2

Here we are going to create a circle. This is something I don’t believe
you can do unless you create it on your own. In other words, it is not
preset.

Now because a circle is just a bunch of lines. That is what we are going
to make. A bunch of lines, close together that go around in a circle.

Now first of all I am going to create a typedef to hold our circle properties
as we call them

typedef struct
{

This will hold the x values as we need them
float x;
This will hold the y values as we need them
float y;
Call it CIRCLE
}CIRCLE;

Then I am setting the property circle to act like the typedef CIRCLE
CIRCLE circle;

Now for the meat of this tutorial, the circle creation function
The properties this takes are:
k – the translation on the y axis
r – the radius of the circle
h – the translation on the x axis

void createcircle (int k, int r, int h) {
Now we begin drawing our lines
glBegin(GL_LINES);
Here we are setting up the vertices all in one line function, this
sets every 2 vertices to a line.

First we begin our loop, this loop will cycle through, constantly
changing our x and y values for our lines. It cycles through until
it reaches 180, increasing in intervals of 1
for (int i = 0;i < 180;i++)
{

Now to set up the current x value that we need for our vertex
I am setting it to the radius of the circle, times by the cosine of
the current value of i, then I am taking h to translate it
circle.x = r * cos(i) – h;
Then to set up the current y value that we need for our vertex
I am setting it to the radius of the circle, times by the sine of
the current value of i, then I am adding k to translate it
circle.y = r * sin(i) + k;
Then I am drawing the vertex
glVertex3f(circle.x + k,circle.y – h,0);

Now for the second part of the line, I am doing the same as above, only
I am moving it 0.1 units so that I am not down to points. This also
Lowers the chance of any holes occuring in the circle
circle.x = r * cos(i + 0.1) – h;
circle.y = r * sin(i + 0.1) + k;

Then I am drawing the vertex
glVertex3f(circle.x + k,circle.y – h,0);
}
glEnd();
}

Then to call it, use something like this:
createcircle(0,10,0);

That would create a circle that is originated around (0,0) and with
a radius of 10 units.

Changing GL_LINES to another shape can come up with some other little
effects if you would like to give it a go.

But other than that, this is all you need for a circle.

If you have any queries, please 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.
  #include <GL/gl.h>
#include <GL/glut.h>
#include <math.h>

typedef struct
{
float x;
float y;
}CIRCLE;

CIRCLE circle;

float rot = 0;

void createcircle (int k, int r, int h) {
    glBegin(GL_LINES);
    for (int i = 0; i < 180; i++)
    {
    circle.x = r * cos(i) - h;
    circle.y = r * sin(i) + k;
    glVertex3f(circle.x + k,circle.y - h,0);
    
    circle.x = r * cos(i + 0.1) - h;
    circle.y = r * sin(i + 0.1) + k;
    glVertex3f(circle.x + k,circle.y - h,0);
    }
    glEnd();
}

void display (void) {
    glClearColor (0.0,0.0,0.0,1.0);
    glClear (GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    glTranslatef(0,0,-20);
    glRotatef(rot,0,1,0);
    glRotatef(rot,1,0,0);
    glRotatef(rot,0,0,1);
    glColor3f(1,1,1);
    createcircle(0,10,0);
    glRotatef(rot,0,1,0);
    glRotatef(rot,1,0,0);
    glRotatef(rot,0,0,1);
    glColor3f(1,0,0);
    createcircle(-2,8,-2);
    glRotatef(rot,0,1,0);
    glRotatef(rot,1,0,0);
    glRotatef(rot,0,0,1);
    glColor3f(0,1,0);
    createcircle(-1,6,-1);
    glRotatef(rot,0,1,0);
    glRotatef(rot,1,0,0);
    glRotatef(rot,0,0,1);
    glColor3f(0,0,1);
    createcircle(2,4,2);
    glRotatef(rot,0,1,0);
    glRotatef(rot,1,0,0);
    glRotatef(rot,0,0,1);
    glColor3f(0,1,1);
    createcircle(1,2,1);
    glutSwapBuffers();
    rot++;
}

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

int main (int argc, char **argv) {
    glutInit (&argc, argv);
    glutInitDisplayMode (GLUT_DOUBLE);
    glutInitWindowSize (500, 500);
    glutInitWindowPosition (100, 100);
    glutCreateWindow (“A basic OpenGL Window);

    glutDisplayFunc (display);
    glutIdleFunc (display);
    glutReshapeFunc (reshape);
    glutMainLoop ();
    return 0;
}

VN:F [1.9.3_1094]
Please rate so I know where to improve the site. 1 means needs a lot of improvement, 10 means perfect. If you leave a low rating, please state why. I don't want people just coming to bash the site.
Rating: 7.0/10 (10 votes cast)
VN:F [1.9.3_1094]
Rating: -2 (from 2 votes)
Improve the web with Nofollow Reciprocity.