CPSC 453
Lab 2 - GLUT Revisited
In this lab, we'll play around a bit with the functionality offered by OpenGL/GLUT, including:
First of all, grab the code for the basic GLUT application.
Download these to your chosen directory and make sure you can compile and run the program.
$> make $> ./helloGL
As mentioned in the first lab, there are 3 main primitives offered by OpenGL: points, lines, and polygons. From these primitives, you can render an incredible variety of objects. To remind yourself of the various primitives, consult the figure below (from Chapter 2 of the Red Book):

Task
- Replace the rendering code in
helloGLwith code that draws a colored square.
Solution

// In DrawGL()
glColor3f(1, 0, 0); // Set color to red
glBegin(GL_QUADS); // Draw square with quad
glVertex2f(-0.5, -0.5);
glVertex2f(-0.5, 0.5);
glVertex2f(0.5, 0.5);
glVertex2f(0.5, -0.5);
glEnd();
Besides squares and lines, there are a few primitive graphics objects that are useful to have at hand, to render easily at any time. Thankfully, the GL extensions (GLU and GLUT) offer a variety of 3D primitives. Some examples:
- Cube
glutWireCube(float size)(or glutSolidCube(float) for filled faces) - Sphere
glutWireSphere(float, int, int)(equiv. glutSolidSphere(...)) - Cylinder
gluCylinder(...)
The GLUT primitives are dead easy to use in an application: just call the function with desired parameters, and optionally set some states (color, texture) beforehand. Try dropping a wireframe cube into helloGL:
// In DrawGL()
glutWireCube(1.0);
GLU primitives require a bit more work to use, as you have to instantiate a class called GLUquadric.
// In DrawGL() GLUquadric* q = gluNewQuadric(); // Note that its bad form to gluCylinder(q, 0.5, 0.5, 1, 10, 5); // allocate and free memory gluDeleteQuadric(q); // every time the display is refreshed!
Task
- How many other GLU/GLUT primitives can you find?
Solution
- Draw a cube and a square at the same time but not overlapping or intersecting.
Solution
Try putting this code into DrawGL(), then compile and run it.

// In DrawGL()
glPushMatrix();
glTranslatef(-1, 0, 0);
glutWireCube(0.7);
glPopMatrix();
glPushMatrix();
glTranslatef(1, 0, 0);
glutWireSphere(0.7, 10, 10);
glPopMatrix();
- Draw a wire cube in a way that shows all edges.
Solution

// In DrawGL()
glRotatef(30, 0, 1, 0);
glRotatef(30, 1, 0, 0);
glutWireCube(1);
Grabbing keyboard input is very easy in GLUT, and allows you to bind keys to various functions in your program. All you need to do is create a callback function conforming to the GLUT key-event prototype, and then register the callback with GLUT.
The callback has the form:
void glutKeyboardFunc(void (*func) (unsigned char key, int x, int y));
So let's add a function to helloGL that simply prints the value of the argument:
// New function
void KeyPress(unsigned char k, int x, int y)
{
std::cout << "You pressed " << k << std::endl;
}
Note that this requires
#include <iostream>
to compile.
The next step is to register the callback with GLUT. This is done with a call to glutKeyboardFunc(...) in main():
// in main()
glutKeyboardFunc(KeyPress);
Make these changes, compile your program, and verify that it works.
Task
- Modify the program so that pressing 't' will toggle the rendering of a wireframe sphere.
Solution
First you should declare a global variable for showing or hiding the sphere.
// global bool showSphere = true;
Then add conditional rendering in DrawGL():
// In DrawGL()
if (showSphere) glutWireSphere(1, 10, 10);
Finally, toggle the value of showSphere in KeyPress():
// In KeyPress()
if (c == 't')
showSphere = !showSphere;
Compile and run this code. Did it work?? It shouldn't! Why not? There is one final thing to do: tell GLUT that it needs to redraw the scene.
Recall that DrawGL() is a callback function, only called when a redraw event occurs. Left to its own devices, GLUT only sends this event when the window is created, resized, or minimized/maximized. You can, however, fire the event yourself, with a call to glutPostRedisplay(). Add this to the KeyPress() function and you should find that the sphere now toggles correctly.
Mouse input is similarly easy to acquire from GLUT. In this case, the callback function looks like
void glutMouseFunc(void (*func)(int button, int state, int x, int y));
where button is one of GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, or GLUT_RIGHT_BUTTON, and state is one of GLUT_UP or GLUT_DOWN. The x and y parameters give the position of the mouse cursor in window coordinates (we'll talk more about this later).
For now, let's just write a function that prints out the position of the mouse when the left button is clicked.
// New function
void MouseEvent(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON &&
state == GLUT_DOWN)
std::cout << "You clicked at <" << x << ", " << y << ">\n";
}
To register the callback, you pass the function pointer to glutMouseFunc():
// in main()
glutMouseFunc(MouseEvent);
Compile and run the code, verifying that you are getting output when clicking in the GL window.
Task
- What can you determine about the window coordinates from the output?
Solution
If you click near the top-left corner, you'll get a mouse coordinate near to <0, 0>. Clicking near the bottom gives you coordinates near to <500, 500> or whatever the window size is set to. Therefore the x coordinate increase from left to right (as usual), but the y coordinate increases from top to bottom. This is often a source of confusion when trying to map mouse input into OpenGL coordinates, so keep it in mind.
Further reading
- List of all GLUT callbacks [opengl.org]
- GLUT keyboard [lighthouse3d.com]
- GLUT mouse [lighthouse3d.com]
