Figure 1 is a simple GLUT program that draws
a lighted sphere. The program merely renders the sphere
when the window needs redrawing. But the example demonstrates the
basics of opening a window with GLUT and rendering an
image into it.
Briefly, I'll explain each GLUT call in the example.
While this example doesn't use it, glutCreateWindow actually
returns an integer identifier for the window. For an application
with multiple windows, this lets you control multiple windows.
Because of GLUT's design, you will rarely
need a window's identifier. This is because GLUT keeps
a current window as part of its state. Most window
operations implicitly affect the current window.
When a window is created, the current window is implicitly set
to the new window.
For example:
Also notice that OpenGL routines that initialize OpenGL's state
in gfxinit() implicitly affect the OpenGL context for
the current window.
If you do need to change the current window, you can call
glutSetWindow(winnum), where winnum is a window identifier.
You can also call glutGetWindow() that returns the
current window identifier.
Whenever a callback is made for
a specific window, the current window is implicitly set to
the window prompting the callback. So when the display callback
registered with glutDisplayFunc is called,
we know that GLUT has implicitly changed the current window
to the window that needs to be redisplayed. This means display can call OpenGL rendering routines and the routines
will affect the correct window.
Supporting more than one window is easy. To
create a second window with the same red sphere add
the following calls before glutMainLoop:
#include <GL/glut.h>
GLfloat light_diffuse[] = { 1.0, 0.0, 0.0, 1.0 };
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
GLUquadricObj *qobj;
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glCallList(1); /* render sphere display list */
glutSwapBuffers();
}
void gfxinit(void)
{
qobj = gluNewQuadric();
gluQuadricDrawStyle(qobj, GLU_FILL);
glNewList(1, GL_COMPILE); /* create sphere display list */
gluSphere(qobj, /* radius */ 1.0, /* slices */ 20, /* stacks */ 20);
glEndList();
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
gluPerspective( /* field of view in degree */ 40.0, /* aspect ratio */ 1.0,
/* Z near */ 1.0, /* Z far */ 10.0);
glMatrixMode(GL_MODELVIEW);
gluLookAt(0.0, 0.0, 5.0, /* eye is at (0,0,5) */
0.0, 0.0, 0.0, /* center is at (0,0,0) */
0.0, 1.0, 0.); /* up is in positive Y direction */
glTranslatef(0.0, 0.0, -1.0);
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutCreateWindow("sphere");
glShadeModel(GL_FLAT);
glutDisplayFunc(display);
gfxinit();
glutMainLoop();
}
Figure 1: Program using GLUT to render a lighted sphere
using OpenGL. The GLUT specific lines are in bold; notice that
very little GLUT-code is required.
glutInit(&argc, argv);
This routine initializes GLUT. Most importantly, it
processes any command line arguments GLUT understands (for X, this
would be options like -display and -geometry). Any
command line arguments recognized by GLUT are stripped
out, leaving the remaining options for your program to process.
Every GLUT program should call glutInit before
any other GLUT routine.
glutInitDisplayMode(GLUT_DOUBLE |
GLUT_RGB | GLUT_DEPTH);
When a window is created, its type is determined by GLUT's
current display mode. The display mode is a set of indicators
that determine the frame buffer capabilities of the window.
The call to glutInitDisplayMode means a subsequently created window should
be created with the following capabilities:
Other indicators like
GLUT_STENCIL could be combined into the display mode value to
request additional capabilities like a stencil buffer.
glutCreateWindow("sphere");
Create a window for OpenGL rendering named ``sphere.'' If a connection to the X server
hasn't yet been made, glutCreateWindow will do so and make
sure that OpenGL is supported by the X server. Details such as picking the
correct visual and colormap and communicating information like the
window name to the window manager are handled by GLUT. Along with the
window, an associated OpenGL rendering context is created. This means
each window has its own private set of OpenGL state.
glutDisplayFunc(display);
will register the display function as the routine to be
called when the current (and just created) window needs to be drawn.
glutMainLoop();
This routine serves the same purpose as the X Toolkit's XtAppMainLoop
routine; it begins event processing and maps any windows that
have been created. It never exits. Callback functions registered with
GLUT are called as necessary. For example, the display routine
in the example will be called whenever Expose events are received
by the program from the X server.
glutSwapBuffers();
When we created the window, the display mode was set to request a
double buffered window. After display renders the sphere using
OpenGL routines, glutSwapBuffers is called to swap the current
window's buffers to make the image rendered into the back buffer
visible. So the sphere is displayed without the program's user seeing
the rendering in progress.
glutCreateWindow("a second window");
glutDisplayFunc(display);
gfxinit();
Because of the implicit update of the current window, display
will also be registered for the second window and
gfxinit will initialize the second window's OpenGL context.
And remember that when a callback like display
is called, the current window is implicitly set to the window needing the callback,
so the rendering done by display will be directed into the
correct window. The result of the output of the resulting program
can be seen in Figure 2.
Figure 2: The two windows generated by the modified version of code in Figure 1.