Debugging OpenGL With GLUT
Debugging OpenGL With GLUT
Because C++ is designed to be portable, it does not provide a graphics library; indeed, some
platforms C++ is implemented on may not even have a hardware device capable of graphics
output. And even if they all did, the graphical hardware capabilities from one platform to the
other will vary so much that it would be a useless attempt to try and create a standard graphical
interface. Thus graphics are left to 3rd party libraries specific to the platform. OpenGL (Open
Graphics Library) is one such library that has been implemented on a variety of platforms
capable of interactive polygonal based computer graphics, such as Windows, Mac, Linux, and
Playstation. The goal of this tutorial is to provide the reader with the opportunity to practice
C++ programming by doing some basic 2D graphics programming in OpenGL. The only
prerequisites correspond to Chapters 1-5 of C++ Module I.
1. Move glut.h to the OpenGL folder of VS05: for example, C:\Program Files\Microsoft
Visual Studio 8\VC\PlatformSDK\Include\gl. Now VS05 will be able to find glut.h when
we write #include <GL/glut.h>.
2. Move the GLUT library file glut32.lib to the VS05 library folder, for example,
C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\Lib. Now VS05 will be
able to find glut32.lib when we link it.
www.gameinstitute.com Page 1
2D OpenGL and GLUT Tutorial
After completing these three steps, your system should now be ready to write and build OpenGL
and GLUT applications.
Note: It is not explicitly necessary to link opengl32.lib, glu32.lib, glut32.lib, since glut.h does this with #pragma:
#pragma comment (lib, "opengl32.lib") /* link with Microsoft OpenGL lib */
#pragma comment (lib, "glu32.lib") /* link with Microsoft OpenGL Utility lib */
#pragma comment (lib, "glut32.lib") /* link with Win32 GLUT lib */
§2 Initializing OpenGL
The following code creates a window using GLUT and clears its area to the color blue. We
recommend that you create a new console application and enter in this code yourself, after
completing the steps in §1.
Program 1: InitGL
#include <GL/glut.h>
void Display()
{
glClear(GL_COLOR_BUFFER_BIT);
glutSwapBuffers();
}
glutDisplayFunc(Display);
glutMainLoop();
}
This program does not do much, but it demonstrates how to create a window using GLUT which
we can draw onto using OpenGL. Every OpenGL program we make in these tutorials will
contain these lines of code; so in some sense, this is the simplest OpenGL program.
We summarize the functions used in Program 1, starting in the main function:
glutInit: This function initializes GLUT and processes command line arguments; it
should be called before any other GLUT functions.
www.gameinstitute.com Page 2
2D OpenGL and GLUT Tutorial
o GLUT_DOUBLE: This means the display system will use a technique called
double buffering for smooth flicker free animation. In double buffering, there are
two surfaces (essentially a rectangular array of pixels) used for drawing: a front
buffer and a back buffer. The front buffer corresponds to the pixels currently
visible on the screen. The back buffer is an off screen surface that just exists in
memory. The idea of hardware double buffering is to draw the next frame of
animation to the back buffer while the front buffer is being displayed. Once the
frame is completely drawn to the back buffer, the front buffer and back buffer are
swapped between monitor refreshes (the old back buffer becomes the new front
buffer and the old front buffer becomes the new back buffer). In this way, we do
not modify the front buffer while the monitor is in the middle of displaying it,
which could cause tearing; additionally, it means that we do not watch the frame
as it gets drawn, which can cause flickering due to overdraw. Note that even
though we always draw to the back buffer, we will sometimes say “draw to the
screen” because that is the essential result in the end.
o GLUT_RGB: This means a pixel‟s color is specified as a combination of red,
green, and blue.
glutInitWindowSize: This function is used to specify the width and height of the
window we are going to create. The first parameter is the width and the second
parameter is the height, and units are measured in pixels. Try experimenting with
different window dimensions.
glutCreateWindow: This function actually creates the window with position and
dimensions specified by glutInitWindowPosition and
glutInitWindowSize; it takes a string parameter which will be displayed in the
window‟s title bar. This function creates a window, but it is not displayed until
glutMainLoop is invoked.
glClearColor: This OpenGL function only sets the color to use for clearing the back
buffer; it does not actually clear it. A color consists of four components: red, green, blue,
and alpha. The alpha component is used to control transparency, something we will not
use in these tutorials. By combining different intensities of red, green, and blue, we can
represent just about every practical color. The first, second, third, and fourth parameters
of this function specify the intensities of the red, green, blue, and alpha components,
www.gameinstitute.com Page 3
2D OpenGL and GLUT Tutorial
glutSwapBuffers: This function instructs the system to swap the front and back
buffers. We must call this function so that the scene we just drew to the back buffer
eventually gets displayed.
Note: GLUT functions are prefixed with “glut” (e.g., glutSwapBuffers). OpenGL functions are prefixed with
“gl” (e.g., glClear). There is another related library, called GLU (OpenGL Utility), and their functions are
prefixed with “glu” (e.g., gluPerspective).
www.gameinstitute.com Page 4
2D OpenGL and GLUT Tutorial
Program 2: GLLines
#include <GL/glut.h>
void Display()
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_LINES);
glEnd();
glutSwapBuffers();
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-VIEW_WIDTH/2, VIEW_WIDTH/2,
-VIEW_HEIGHT/2, VIEW_HEIGHT/2,
-10.0, 10.0);
}
glutReshapeFunc(Reshape);
glutDisplayFunc(Display);
glutMainLoop();
}
www.gameinstitute.com Page 5
2D OpenGL and GLUT Tutorial
You will need to type this program into your code editor and build the program, in order to do
the exercises of this section. Now let us review the new code Program 2 introduces, starting in
the Display function.
OpenGL only has built-in support for drawing simple geometric objects like points, lines,
and triangles (complex objects that you see in games are modeled by a mesh of many small
triangles). Before we can specify the vertices of any geometric primitives, we need to call
glBegin. The single parameter of glBegin is used to specify the type of primitives we will
be drawing; in the example above, we specify GL_LINES indicating that we are drawing a line
list. With a line list, every two specified vertices form a line segment; a vertex is specified by
the glVertex2f function, which takes two floating-point parameters: the x- and y-coordinates
of the vertex, respectively. On the other hand, specifying GL_TRIANGLES for glBegin
would indicate a triangle list, in which every three vertices specified by glVertex2f would
define a triangle. When we are finished drawing the primitives of a particular type, we must call
glEnd. Note that all glVertex2f function calls must take place between a
glBegin/glEnd pair.
The Reshape function is a function we define and pass to GLUT via the
glutReshapeFunc (i.e., we pass a pointer to the function Reshape as an argument to the
function glutReshapeFunc). GLUT will then invoke this function whenever the window is
resized, passing in the new width and height of the resized window. Inside the Reshape
function, we update some OpenGL display properties based on the new window dimensions.
The way we call glViewport ensures that OpenGL will draw to the entire newly resized
window (rather than a sub-rectangle of the window). The glOrtho function, together with
glMatrixMode and glLoadIdentity, define a “viewing box,” centered at the origin.
Geometry that intersects this box can be seen by the virtual eye, and will be displayed on the
screen. Geometry outside this box is not seen and is discarded. The first two parameters specify
the left and right bounds of the box (where the x-axis corresponds with left and right); the third
and fourth parameters specify the bottom and top bounds of the box (where the y-axis
corresponds with bottom and top); and the fifth and sixth parameters specify the near and far
bounds of the box (where the z-axis corresponds with near and far). Because we are only
drawing in 2D for these tutorials, the fifth and sixth parameters, controlling the depth of the box,
are not of particular importance. The aspect ratio code is used to scale the view box so that its
width and height ratio matches the width and height ratio of the output window; if the ratios do
not match, a scaling distortion occurs (e.g., a circle becomes an ellipse or a square becomes a
rectangle). In the code, we specify the height of the view box with the global variable
VIEW_HEIGHT and then compute the width of the view box, VIEW_WIDTH, in the Reshape
function based on the aspect ratio.
Exercise: Try replacing the Display code in Program 2 with the following code to draw the
lines with color.
void Display()
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_LINES);
www.gameinstitute.com Page 6
2D OpenGL and GLUT Tutorial
glEnd();
glutSwapBuffers();
}
The new glColor3f function has three floating-point parameters (the ranges of which should
be in the interval [0, 1], just like glClearColor) which specify the red, green, and blue
intensities of the color to use, respectively. Subsequently drawn vertices will be drawn with the
color specified by this function.
Exercise: Try replacing the Display code in Program 2 with the following code to draw a grid
of points.
void Display()
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_POINTS);
glEnd();
glutSwapBuffers();
}
Exercise: Try replacing the Display code in Program 2 with the following code to draw a
triangle.
void Display()
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
www.gameinstitute.com Page 7
2D OpenGL and GLUT Tutorial
glVertex2f(-5.0f, -5.0f);
glEnd();
glutSwapBuffers();
}
Notice how we draw each vertex with a different color. The colors of triangle‟s interior points
are formed by a weighted average of these three vertex colors to create a smooth transition. We
recommend you experiment with different colors at each vertex and examine the output until you
develop some intuition of how the interior colors are related to the vertex colors.
Exercise: Try replacing the Display code in Program 2 with the following code to draw a
quad (rectangle).
void Display()
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS);
glEnd();
glutSwapBuffers();
}
Exercise: The following two functions draw a circle and solid disk, respectively. We will not go
into the details of how these functions work (for understanding the math, try the Game Math
course). However, we will make use of these functions in later demos, so you ought to try and
use these functions to draw a few circles and disks, in order to make sure you know how to use
them.
#include <cmath>
// DrawCircle
// cx: x-coordinate of the center of the circle.
// cy: y-coordinate of the center of the circle.
// r: radius of the circle.
// RGB: Color components of the circle's color.
www.gameinstitute.com Page 8
2D OpenGL and GLUT Tutorial
glColor3f(R, G, B);
glVertex2f(x,y);
}
glEnd();
}
// DrawDisk
// cx: x-coordinate of the center of the disk.
// cy: y-coordinate of the center of the disk.
// RGB: Color components of the disk's color.
void DrawDisk(float cx, float cy, float r, float R, float G, float B)
{
glBegin(GL_TRIANGLE_FAN);
glColor3f(R, G, B);
glVertex2f(x,y);
}
glEnd();
}
www.gameinstitute.com Page 9
2D OpenGL and GLUT Tutorial
case 'd':
// Respond to ‘d’ key press.
break;
case 'w':
// Respond to ‘w’ key press.
break;
case 's':
// Respond to ‘s’ key press.
break;
}
The first parameter specifies the key pressed, and the second and third parameters specify the
position of the mouse cursor when the key was pressed, relative to the top-left corner of the
window. So essentially our key handling function will just be a case statement which does
different things based on which key was pressed. Often, and in the case of our demo for this
section, we will use key presses to update the position of an object we are drawing. Therefore,
after updating the position based on keyboard input, we call glutPostRedisplay to instruct
GLUT to repaint the window, so that the object is drawn in its updated position. After defining a
key handling function like the one above, we need to register it with GLUT, so that GLUT can
invoke it, with the appropriate arguments, when it detects that a key is pressed; to do this, call the
glutKeyboardFunc and pass a pointer to the key handing function you want to use:
glutKeyboardFunc(OnKeyPress);
The first parameter identifies the button pressed; as you can see from the code, there are three
predefined identifiers GLUT uses to identify the three mouse buttons. The state parameter is
GLUT_UP if the mouse event was due to a mouse button being released or GLUT_DOWN if the
www.gameinstitute.com Page 10
2D OpenGL and GLUT Tutorial
mouse event was due to a mouse button being pressed (this flexibility is for if you want to do
different things for mouse presses and mouse releases). The third and fourth parameters specify
the position of the mouse cursor when the mouse button was pressed/released, relative to the top-
left corner of the window. After defining a mouse handling function like the one above, we need
to register it with GLUT, so that GLUT can invoke it, with the appropriate arguments, when it
detects that a mouse button is pressed; to do this, call the glutMouseFunc and pass a pointer
to the mouse handing function you want to use:
glutMouseFunc(OnMouseClick);
The following program draws a disk, where the user can press the „a‟, „d‟, „w‟, and „s‟
keys to move the disk to the left, right, up, and down, respectively; in addition, the user can left
mouse click a point in the window and the disk will be moved to the clicked point. The code
relevant to input handling and updating the disk has been bolded.
Program 3: GLInput
#include <GL/glut.h>
#include <cmath>
//************************************************************************
// Globals
//************************************************************************
// Function prototypes
// DrawDisk
// cx: x-coordinate of the center of the disk.
// cy: y-coordinate of the center of the disk.
// RGB: Color components of the disk's color.
void DrawDisk(float cx, float cy, float r, float R, float G, float B);
//************************************************************************
// Function implementations.
www.gameinstitute.com Page 11
2D OpenGL and GLUT Tutorial
void Display()
{
glClear(GL_COLOR_BUFFER_BIT);
glEnd();
glutSwapBuffers();
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-VIEW_WIDTH/2, VIEW_WIDTH/2,
-VIEW_HEIGHT/2, VIEW_HEIGHT/2,
-10.0, 10.0);
}
www.gameinstitute.com Page 12
2D OpenGL and GLUT Tutorial
}
}
glutReshapeFunc(Reshape);
glutKeyboardFunc(OnKeyPress);
glutMouseFunc(OnMouseClick);
glutDisplayFunc(Display);
glutMainLoop();
}
glColor3f(R, G, B);
glVertex2f(x,y);
}
glEnd();
}
vx = +float(sx)*VIEW_WIDTH/screenWidth - VIEW_WIDTH/2;
vy = -float(sy)*VIEW_HEIGHT/screenHeight + VIEW_HEIGHT/2;
}
Remark: The ScreenToView function deserves some explanation. When a mouse button is pressed, we are given
the coordinates of the clicked point, relative to the upper-left corner of the window. These coordinates, however, are
not directly useful for updating the position of the disk because we describe the position of the disk relative to the
view box coordinate system, not the window coordinate system. So what the ScreenToView function does is
convert the window coordinates to the corresponding view coordinates, much like meters can be
www.gameinstitute.com Page 13
2D OpenGL and GLUT Tutorial
converted to centimeters, for example. However, for the purposes of this tutorial, we will omit the mathematical
derivation. Just know that the function inputs a point in window coordinates (sx and sy) and outputs the
corresponding point in view coordinates (vx and vy).
If we think of x and y as the coordinates of a point, then this pair of equations generates different
points as we vary t in the range 0 to 1. For example, the following table shows the
different points generated for different values of t:
If we plot these functions using graphing software, we see that these equations generate points on
a circle, centered at the origin with radius 10, starting at the point . If we think of as
time, and the position of an object, then we can think of these equations as defining the
motion of the object over time; in the example above, the object would move around a circle
over a period of one time unit (say, seconds).
In the above equations, we kept the radius of the circle fixed at 10, but we could also
make the radius vary as t varies. For example, we could make the radius oscillate to get a spiral
like effect:
In this section, we animate a disk by changing its position and radius over time using
equations like the ones described above. There are two key GLUT functions we need to add
animation:
glutIdleFunc: This function is used to register a function for GLUT to call when the
application is idle. Our application will be idle most of the time since, once the window
www.gameinstitute.com Page 14
2D OpenGL and GLUT Tutorial
is created and we draw our scene, the application will just sit there idling. In the demo
below, we register the Display function as the idle function, so that the image will be
redrawn at frequent intervals. Thus, if we change the image over time, we will
continuously be redisplaying the new updated image, thereby creating a smooth
animation.
The following program presents the complete code for animating the disk; as usual, the code
relevant to this section has been bolded.
Program 4: GLTiming
#include <GL/glut.h>
#include <cmath>
//************************************************************************
// Globals
//************************************************************************
// Function prototypes
// DrawDisk
// cx: x-coordinate of the center of the disk.
// cy: y-coordinate of the center of the disk.
// RGB: Color components of the disk's color.
void DrawDisk(float cx, float cy, float r, float R, float G, float B);
//************************************************************************
// Function implementations.
void Display()
{
glClear(GL_COLOR_BUFFER_BIT);
www.gameinstitute.com Page 15
2D OpenGL and GLUT Tutorial
float y = r*sinf(2*PI*t);
glEnd();
glutSwapBuffers();
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-VIEW_WIDTH/2, VIEW_WIDTH/2,
-VIEW_HEIGHT/2, VIEW_HEIGHT/2,
-10.0, 10.0);
}
glutMainLoop();
}
glColor3f(R, G, B);
glVertex2f(x,y);
}
glEnd();
www.gameinstitute.com Page 16
2D OpenGL and GLUT Tutorial
Exercise: Try replacing the Display function in Program 4 with this new one:
void Display()
{
glClear(GL_COLOR_BUFFER_BIT);
glVertex2f(75.0f*x, 75.0f*y);
glEnd();
glutSwapBuffers();
}
www.gameinstitute.com Page 17
2D OpenGL and GLUT Tutorial
Figure 1
Exercise: Try replacing the Display function in Program 4 with this new one:
void Display()
{
glClear(GL_COLOR_BUFFER_BIT);
www.gameinstitute.com Page 18
2D OpenGL and GLUT Tutorial
glVertex2f(4.0f*x, 4.0f*y);
glEnd();
glutSwapBuffers();
}
Note: Given a list of n + 1 vertices , GL_LINE_STRIPS draws a line between each vertex and its
subsequent vertex . More explicitly, a line segment is drawn between and , between and , …,
between and . Observe how the line segments are connected. Contrast this to GL_LINES where the line
segments need not be connected.
Figure 2
www.gameinstitute.com Page 19
2D OpenGL and GLUT Tutorial
Figure 3
www.gameinstitute.com Page 20