ANSI C, C++, VB and FORTRAN bindings for writing window system
independent OpenGL programs. It was written by Mark J. Kilgard and
covers a great hole left by the OpenGL specification. Thanks to GLUT
developers we can use a common window system interface independently
of the target platform. OpenGL applications using GLUT can be easily
ported between platforms without having to introduce numerous changes
to the source code. GLUT definitely simplifies the production of
OpenGL code and it complements the OpenGL library.
The GLUT API is a state machine like OpenGL. This means that GLUT
has a number of state variables that live during the execution of the
application. The initial states of the GLUT machine has been reasonably
chosen to fit most applications. The program can modify the values of
the state variables as it sees fit. Whenever a GLUT function is invoked
its action is modified according to the values of the state variables.
GLUT functions are simple, they take few parameters. No pointers are
returned and the only pointers passed to GLUT functions are pointers to
character strings and opaque font handles.
GLUT functions can be classified into several sub-APIs according to
their functionality:
Initialization
Beginning Event Processing
Window Management
Overlay Management
Menu Management
Callback registration
Color Index Colormap Management
State Retrieval
Font Rendering
Geometric Shape Rendering
Initializations
Every OpenGL program using GLUT must begin by initializing the
GLUT state machine. The glut initialization functions are prefixed by
glutInit-. The main initialization routine is glutInit:
Usage:
glutInit(int **argcp, char **argv);
argcp is a pointer to the program's unmodified argc variable from
main. Upon return, the value pointed to by argcp is updated because
glutInit extracts any command line options relevant for the GLUT
library, for example: under the X Window System environment, any
options relevant for the X window associated to the GLUT window.
argv is the program's unmodified argv variable for main.
glutInit takes care of initializing the GLUT state variables and
negotiating a session with the window system. There are a few routines
that could appear before glutInit; only routines prefixed by
glutInit-. These routines can be used to set the default window
initialization state. For example:
Usage:
glutInitWindowPosition(int x, int **y);
glutInitWindowSize(int width, int **height);
x,y = screen position in pixels of the window (upper left corner)
width,height in pixels of the window.
There is another initialization routine always present in every OpenGL
application, glutInitDisplayMode():
Usage:
glutInitDisplayMode(unsigned int mode);
mode is the Display mode, a bitwise OR-ing of GLUT display mode bit masks.
The possible bitmask values are:
GLUT_RGBA
Select an RGBA mode window. This is the default if
neither GLUT_RGBA nor GLUT_INDEX are
specified.
GLUT_RGB
same as GLUT_RGBA.
GLUT_INDEX
Select color index window mode. This overrides
GLUT_RGBA.
GLUT_SINGLE
Select a single buffered window. This is the default.
GLUT_DOUBLE
Select a double buffered window. This overrides
GLUT_SINGLE.
GLUT_ACCUM
Select a window with an accumulation buffer.
GLUT_ALPHA
Select a window with an alpha component to the color
buffer(s).
GLUT_DEPTH
Select a window with a depth buffer.
GLUT_STENCIL
Select a window with a stencil buffer.
GLUT_MULTISAMPLE
Select a window with multismapling support.
GLUT_STEREO
Select a stereo window.
GLUT_LUMINANCE
Select a stereo window with a "luminance" color
model.
#include <GL/glut.h>
void main(int argcp, char **argv){
/* Set window size and location */
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);
/* Select type of Display mode:
Single buffer & RGBA color */
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
/* Initialize GLUT state */
glutInit(&argcp, argv);
.....more code
};
Second an example of an animation program:
#include <GL/glut.h>
void main(int argcp, char **argv){
/* Set window size and location */
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);
/* Select type of Display mode:
Double buffer & RGBA color */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
/* Initialize GLUT state */
glutInit(&argcp, argv);
.....more code
};
Event Processing
As mentioned before, GLUT is a state machine. Now we will learn it is
also designed as an event driven engine. This means that there is a
"timer" or continuous loop that gets started after the proper
initializations and that processes, one by one, all the events declared to
GLUT during initialization. Events are: a mouse being clicked, a window
closed, a window reshape, a cursor moved, keyboard keys pressed, and
even more curiously the "idle" event, i.e. nothing happens! Each one of
the possible events must be registered in one of the GLUT state variables
for the "timer" or event processing loop of GLUT to periodically check
whether that event has been triggered by the user.
For example, we could register "click mouse button" as an event for
GLUT to watch out for. Events are registered through callback
registration routines. All have the syntax glut[someEvent]Func, in
the case of the mouse clicking it would be glutMouseFunc. A callback
registration tells the GLUT engine which user-defined function is to be
called if the corresponding event is triggered. So, if I write my own
routine MyMouse which specifies what to do if the left mouse button is
clicked, (or the right, etc.) then I can register my callback function after
the glutInit() in main() using the statement
"glutMouseFunc(MyMouse);" .
Let us leave for later which callback functions and events are permitted
in GLUT. The important thing now is that after all the important events
in our application have been registered we must invoke the event
processing routine of GLUT, namely glutMainLoop(). The function
never comes back, our program basically enters an infinite loop. It will
call as necessary any callbacks that have been previously registered.
Every main() for an OpenGL application must then end in a
glutMainLoop() statement. So in the case of our animation template:
#include <GL/glut.h>
void main(int argcp, char **argv){
/* Initialize GLUT state */
glutInit(&argcp, argv);
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);
/* Open a window */
glutCreateWindow("My OpenGL Application");
/* Select type of Display mode:
Double buffer & RGBA color */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
/* Register Callback Functions */
.....
/* Start Event Processing Engine */
glutMainLoop();
};
Back to events... I want now to introduce two callback registration
functions that are very fundamental in any animation program. The
glutDisplayFunc which sets the display function for the current
window and the glutIdleFunc which sets the idle callback. Both
registration routines expect a function of type void *(void). Say we
write two additional callback functions to our animation template, void
MyDisplay(void) which takes care of invoking the OpenGL
instructions that actually draw our scene onto the window, and void
MyIdle(void) which is a function that gets called whenever there is no
other user input, that is, each time the event processing machine of
GLUT goes once around the infinite loop (glutMainLoop()) and does
not find any new event triggered, it processes MyIdle. Why do I need to
register an Idle callback function in an animation program? Because if
we wish to modify each one of the images (frames) shown during the
animation independently of any user input, there has to be a function (the
idle callback function) that gets called every so often during the life of
the OpenGL program and changes the frames before they get drawn by
Mydisplay().
Animation Example
Finally here is a simple template for an animation program:
#include <GL/glut.h>
void MyIdle(void){
/* Some code to modify the variables defining next
frame */
....
};
void MyDisplay(void){
/* Some OpenGL code that draws a frame */
....
/* After drawing the frame we swap the buffers */
glutSwapBuffers();
};
void main(int argcp, char **argv){
/* Initialize GLUT state */
glutInit(&argcp, argv);
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);
/* Open a window */
glutCreateWindow("My OpenGL Application");
/* Select type of Display mode:
Double buffer & RGBA color */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
/* Register Callback Functions */
glutDisplayFunc(MyDisplay)
glutIdleFunc(MyIdle)
/* Start Event Processing Engine */
glutMainLoop();
};
Notice that at the end of MyDisplay, a new GLUT routine,
glutSwapBuffers(). This is very useful in animations. We are using
a window in DOUBLE buffer mode, one shown and one hidden. The
drawing OpenGL instructions in this case always render into the hidden
buffer. The glutSwapBuffers call, exchanges the buffers, showing in
the window at once what was drawn. This technique is common in
computer animations because it prevents the human eye from seeing the
frame being constructed line by line.
Drawing Points
OpenGL has only a few geometric primitives: points, lines, polygons.
All of them are described in terms of their respective vertices. A vertex
is characterized by 2 or 3 floating points, the Cartesian coordinates of
the vertex, (x,y) in 2D and (x, y, z) in 3D. While Cartesian coordinates
are the most common, in computer graphics there is also the
homogeneous coordinate system in which every point is described by 4
floating points (x, y, z, w). We will come back to them after covering
some elementary notions of 3D rendering.
Since in OpenGL all geometric objects are eventually described as an
ordered set of vertices, there is a family of routines to declare a vertex.
Its syntax is:
void glVertex{234}{sifd}[v](TYPE coords);
Get familiar with this notation. The curly brackets indicate part of the
name of the routine. The routines can take 2, 3 or 4 parameters in either
short, long, float or double type. Optionally these parameters can be
supplied in a vector form, in which case we would use the v-type
routines. Here are some examples:
void glVertex2s(1, 3);
void glVertex2i(23L, 43L);
void glVertex3f(1.0F, 1.0F, 5.87220972F);
float vector[3];
void glVertex3fv(vector);
To simplify all these routines are refered to as glVertex*.
OpenGL interprets any sequence of vertices according to its context. The
context is declared by the pair of routines glBegin(GLenum mode)
and glEnd(), any glVertex* statements executed between the two are
interpreted according to the value of mode, for example:
glBegin(GL_POINTS);
glVertex2f(0.0, 0.0);
glVertex2f(1.0, 0.0);
glVertex2f(0.0, 1.0);
glVertex2f(1.0, 1.0);
glVertex2f(0.5, 0.5);
glEnd();
draws 5 points in 2D with the coordinates specified. GL_POINTS is one
of the labels defined in the OpenGL header file <GL/gl.h>. There are
many other modes available but we will review then as necessary.
Every point is drawn with the color currently stored in the OpenGL state
variable associated with the color buffer. To change the current color,
use the family of routines glColor*; there is a lot to say about selecting
and manipulating colors (there will be another article only on this
subject). For the moment, we will be using three floating point numbers
from 0.0 to 1.0 - this is the RGB (Red-Green-Blue) encoding;
glColor3f(1.0, 1.0, 1.0); /* White */
glColor3f(1.0, 0.0, 0.0); /* Red */
glColor3f(1.0, 1.0, 0.0); /* Magenta */
etc...
Drawing Lines and Polygons
As previously mentioned glBegin(GLenum mode) accepts various
modes and the sequence of vertices v0, v1,v2, v3,v4,... vn-1 declared
afterwards are interpreted accordingly. The possible values for mode and
the actions taken are:
GL_POINTS Draws a points at each of the n vertices.
GL_LINES Draws a series of unconnected lines. The segments
are drawn between v0 and v1, v2 and v3,...etc. If n is odd vn-1 is
ignored.
GL_POLYGON Draws a polygon using v0, v1,..,vn-1 as
vertices. n must be at least 3 or nothing is drawn, also the polygon
can not intersect itself and must be convex (due to the hardware's
algorithm limitations).
GL_TRIANGLES Draws a series of triangles using vertices
v0,
v1 and v2, then v3, v4 and v5 etc. If n is not a multiple of 3 the
remaining points are ignored.
GL_LINE_STRIP Draws a line from v0 to v1, them from v1 to
v2 and so on. Finally from vn-2 to vn-1 for a total of n-1 line
segments. There are no restrictions on the vertices describing a line
strip, lines can intersect arbitrarily.
GL_LINE_LOOP Same as GL_LINE_STRIP except that a final
line segment is drawn from vn-1 to v0, closing the loop.
GL_QUADS Draws a series of quadrilaterals using vertices
v0,
v1, v2, v3 and v4, v5, v6, v7 and so on.
GL_QUAD_STRIP Draws a series of quadrilaterals using
vertices v0, v1, v3, v2 then v2, v3, v5, v4 and so on.
GL_TRIANGLE_STRIP Draws a series of triangles using
vertices in the following order v0, v1, v2, then v2, v1, v3, then v2,
v3, v4, etc. The ordering is to ensure that the triangles has the
correct orientation and the strip can be used to form part of a
surface.
GL_TRIANGLE_FAN Similar to GL_TRIANGLE_STRIP
except that the triangles are v0, v1, v2, then v0, v2, v3, then v0, v3,
v4, and so on. All the triangles have v0 as a common vertix.
As before there is an idle() callback function whose aim here is to keep
the clock running (updating the variable time). The display() draws two
objects; the pendulum cord and weight (in white and red respectively).
The motion of the pendulum coordinates is implicit in the formulas for
xcenter and ycenter:
void display(void){
static double radius = 0.05;
const double delta_theta = pi2/20;
double xcenter , ycenter;
double x, y;
double theta = 0.0;
double current_angle = cos(omega * time);
glColor3f(0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 1.0, 1.0);
/* Draw pendulum cord */
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
glVertex2f(0.0, 0.0);
xcenter = -cord_length * sin(current_angle);
ycenter = -cord_length * cos(current_angle);
glVertex2f(xcenter, ycenter);
glEnd();
/* Draw pendulum dish */
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_POLYGON);
while (theta <= pi2) {
x = xcenter + radius * sin(theta);
y = ycenter + radius * cos(theta);
glVertex2f(x, y);
theta += delta_theta;
};
glEnd();
glutSwapBuffers();
};
void glutWireSphere(GLdouble radius, GLint slices, GLint
stacks);
void glutSolidSphere(GLdouble radius, GLint slices,
GLint stacks);
void glutWireCube(GLdouble size);
void glutSolidCube(GLdouble
size);
void glutWireTorus(GLdouble innerRadius, GLdouble outerRadius,
GLint nsides, GLint rings);
void glutSolidTorus(GLdouble
innerRadius, GLdouble outerRadius,
GLint nsides, GLint
rings);
void glutWireIcosahedron(void);
void
glutSolidIcosahedron(void);
void glutWireOctahedron(void);
void
glutSolidOctahedron(void);
void glutWireTetrahedron(void);
void
glutSolidTetrahedron(void);
void glutWireDodecahedron(GLdouble radius);
void
glutSolidDodecahedron(GLdouble radius);
void glutWireCone(GLdouble radius, GLdouble height, GLint slices,
GLint stacks);
void glutSolidCone(GLdouble radius, GLdouble
height, GLint slices,
GLint stacks);
void glutWireTeapot(GLdouble size);
void
glutSolidTeapot(GLdouble size);