Introduction
For design reasons the OpenGL specification was isolated from any window
system dependencies. The resulting interface is a portable, streamlined
and efficient 2D and 3D rendering library. It is up to the native window
system to open and render windows. The OpenGL library communicates with
the native system through additional auxiliary libraries. For example,
the GLX auxiliary library describes the interaction between OpenGL and
the X window System.
The OpenGL Utility Toolkit (GLUT) is a programming interface with ANSI
C 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.
GLUT is relatively small and easy to learn. It is well designed and
in fact, its author has already written wonderful documentation for it.
Therefore starting a series of articles here in LinuxFocus
seems redundant. We encourage any serious developer to read Mark's documentation.
Our purpose for writing this regular GLUT column is to introduce the GLUT
library and its usage step by step with examples as a companion reading
with the OpenGL series of this magazine. We hope this will make a
useful contribution and motivate more programmers to join the OpenGL-linux
wagon. In any case, get your own copy of Mark's documentation as
a good reference.
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
In this article we will explore some of the initialization, event processing
and window management functions necessary to start a simple OpenGL program.
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 omni-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. |
If some of these features are not familiar to you, don't worry., sooner
or later we will write about them. Let us examine a couple of examples.
First a simple initialization for one shot rendering application:
#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
};
We will come back to these two examples as we continue to learn more
about GLUT. The main difference is that in the second case the display
is initialized in a double buffer mode, ideal for animations because it
eliminates flickering effects while changing frames in the animation sequence.
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();
};
Notice I have added some extra code we never mentioned before. It is
one of GLUT's window management routines, glutCreateWindow(char **name).
This is what I like so much about OpenGL & GLUT design philosophy,
it is pretty clear what the routine does by just looking at the name!.
It also takes care of actually passing the order to the underlying window
system to open a window for our OpenGL application. The window will have
the name "name" passed as a character string. In the X Window Environment
this name is written on the upper left corner of the window. The window
management section of GLUT has many other functions that we will eventually
have a look at. For now, this one is sufficient. I have also rearranged
the initialization routines to show that they can be placed after glutInit().
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 I have added 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.
There is already enough material to start writing OpenGL applications.
The only things missing are the OpenGL instructions in MyDisplay
that actually do the drawing...but that is another story ;-).
In the next article on GLUT programming we will explore in more depth
the functionality available to us in the Window Management section of GLUT,
and how to open multiple scenes inside the same window. We will also
learn about using menus, including the pros and cons for their portability.
|