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 }
Keep up the good work. Your tutorials are still relevant up to this day. Kudos.
Hi,
These tutorials have been great so far! Exactly what I need for helping with my Uni work.
I noticed that the ones before this seem to be more polished; will you get around to updating all the tutorials from here onwards at some point? It seems to have been a long time since you updated the site.
Thanks,
-TechniMan
I wish there was an explanation for glulookat.
Hi the code works with glew.h
and not gl.h
Hi Dhiraj,
That depends on your setup. It should work with gl.h. glew and glut both include gl.h
Cheers,
Swiftless
its better if u have given syntax for gluLookT
Syntax
Copy
void WINAPI gluLookAt(
GLdouble eyex,
GLdouble eyey,
GLdouble eyez,
GLdouble centerx,
GLdouble centery,
GLdouble centerz,
GLdouble upx,
GLdouble upy,
GLdouble upz
);
eyex
The position of the eye point.
eyey
The position of the eye point.
eyez
The position of the eye point.
centerx
The position of the reference point.
centery
The position of the reference point.
centerz
The position of the reference point.
upx
The direction of the up vector.
upy
The direction of the up vector.
upz
The direction of the up vector.
Return Value
This function does not return a value.
Hey prabhakar,
The syntax is available ALL over the internet, so there is no way I am going to rewrite it 😉
Cheers,
Swiftless
A note for those following along on a Linux box. This code will produce an error that looks like:
error: stray ‘\342’ in program
To fix this replace the angled quotes in the program ie: “” with vertical quotes. Just a simple copy and paste thing.
very easy to understand..can you please add something about the lookAt method? it has got lot of parameters, what do they stand for?
thanks
I agree with Khalladay, its great to see well explained, easy to follow tutorials. Thanks!
These tutorials have been awesome. Props for posting an OpenGL tutorial thats actually useful and geared to absolute beginners.