8. OpenGL Buffering (Version 2.0)

Introduction

I guess we should start this tutorial by addressing what was happening in the previous tutorial on rotation and translation. You probably would have noticed that the cube was bouncing up and down and rotating way too fast and for the majority of the time looked broken. This is because we are have GLUT set to single buffering with GLUT_SINGLE. But don’t worry, I’m about to explain the difference between single, double and even triple buffering and why our current cube looks broken and how we can fix it.

Single Buffering

Single buffering works by sending the image OpenGL has generated to the graphics card and then to the monitor as soon as you call glFlush(). In an ideal world, the monitor would show this in real-time as soon as it was sent from the graphics card. Unfortunately in the real-world we have monitor refresh times, this is the speed at which the monitor pushes out a complete image on the screen. If you send images to the monitor outside of its refresh rate, then you are going to be missing quite a few frames as the graphics card can push out images much faster than the monitor can receive them. Because of these two devices are not acting in sync, you find that you don’t see a lot of what is going on and you end up with a garbled mess on the screen.

Double Buffering

But everything else on your monitor is in sync so why is it just my OpenGL context which is acting screwy? Well, this is because Windows is configured to your monitors refresh rate but your application is not. But we can fix this easily as GLUT has support for double buffering through GLUT_DOUBLE.  Combined with swapping out glFlush() for glutSwapBuffers(), we get a nice constant image on the screen. Double buffering gives OpenGL a ‘hidden’ render which it can write to while the previous render is being shown on the screen. Think of it this way, you are drawing an animation using two blackboards in front of a crowd of people, but they only see one blackboard at a time. You draw frame number 1 on blackboard number one and when it’s complete, you swap blackboard number 1 and blackboard number 2. You then erase anything on blackboard number 2 and draw the next frame, once it’s complete you once again swap blackboard number 1 and blackboard number 2.

I think we should start this textbook by looking at what happened in the previous rotation and translation tutorial. In this textbook, you can read more about the medicine viagra and how it can help get rid of the symptoms of erectile dysfunction.

Triple Buffering

Triple buffering takes the same approach as above, only now there are two extra blackboards that you can draw on while the third one is being looked at. Say you are drawing on blackboard number 2 and you finish, while people are still looking at number 3, you can then continue drawing on blackboard number 1, theoretically keeping you one blackboard in front all the time. However this isn’t natively supported by OpenGL or GLUT and is ‘up to the driver’, meaning if you want to enable triple buffering on your application specifically, you have to open up your Nvidia drivers (for example) and select your application and then select triple buffering.

Quad Buffering

But what about taking it even further? Quad buffering?! Well, typically quad buffering refers to double buffering in a stereoscopic environment. Eg: You have two displays, typically for 3D purposes and each eye is double buffered. You can’t really enable actual quad buffering in computer graphics environments as there’s no real point being more than 2 frames ‘in front’. Eventually you will have all the buffers filled and be waiting for the display.

Coding

So what’s involved in enabling quad buffering in your application? Well, the good news is very little at all. We only need to change 2 lines of code thanks the GLUT and FreeGLUT.

The first thing we want to do is jump down into our init() function and replace the line:

glutInitDisplayMode (GLUT_SINGLE); // Set up a basic display buffer (only single buffered for now)

With:

glutInitDisplayMode (GLUT_DOUBLE); // Set up a basic display buffer (now double buffered)

 

And then jump to the end of our display() function and replace:

glFlush(); // Flush the OpenGL buffers to the window

with:

glutSwapBuffers(); // Swap our buffers

And that is literally all it takes to enable double buffering. You are probably wondering why we didn’t just do this from the start and there is a good reason for that. You should now know what double buffering actually is and how to identify single buffering.

If you have any questions, as usual, my email address is swiftless@gmail.com and you are more than welcome to contact me with a serious question.

Tutorial Code

#include <GL/glew.h> // Include the GLEW header file
#include <GL/glut.h> // Include the GLUT header file

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

bool movingUp = false; // Whether or not we are moving up or down
float yLocation = 0.0f; // Keep track of our position on the y axis.

float yRotationAngle = 0.0f; // The angle of rotation for our object

void keyOperations (void) {
if (keyStates[GLUT_KEY_LEFT]) { // If the left arrow key has been pressed
// Perform left arrow key operations
}
}

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); //Clear the colour buffer (more buffers later on)
glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations

glTranslatef(0.0f, 0.0f, -5.0f); // Push eveything 5 units back into the scene, otherwise we won't see the primitive

glTranslatef(0.0f, yLocation, 0.0f); // Translate our object along the y axis

glRotatef(yRotationAngle, 0.0f, 1.0f, 0.0f); // Rotate our object around the y axis

glutWireCube(2.0f); // Render the primitive

glutSwapBuffers(); // Swap our buffers

if (movingUp) // If we are moving up
yLocation -= 0.05f; // Move up along our yLocation
else // Otherwise
yLocation += 0.05f; // Move down along our yLocation

if (yLocation < -3.0f) // If we have gone up too far
movingUp = false; // Reverse our direction so we are moving down
else if (yLocation > 3.0f) // Else if we have gone down too far
movingUp = true; // Reverse our direction so we are moving up

yRotationAngle += 0.1f; // Increment our rotation value

if (yRotationAngle > 360.0f) // If we have rotated beyond 360 degrees (a full rotation)
yRotationAngle -= 360.0f; // Subtract 360 degrees off of our rotation
}

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); // Set up a basic display buffer (now double buffered)
glutInitWindowSize (500, 500); // Set the width and height of the window
glutInitWindowPosition (100, 100); // Set the position of the window
glutCreateWindow ("Your first OpenGL Window"); // Set the title for the window

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

glutIdleFunc(display); // Tell GLUT to use the method "display" as our idle method as well

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
}
  • March 25, 2010
  • 12