Now that you've read the preceding companion piece to this article, "Preparing to Build a OpenGL Application," you're ready to tackle rendering and drawing. Let's jump right in to the details of rendering.

Rendering in OpenGL

The function registered as the Display (and usually Idle) callback
is where control is passed whenever it's time to draw the next frame
of the display. For our program this is the cbRenderScene()
function, and it's also where
our motion calculations are done.

Figure 1. With different drawing mode settings, a wide range of visual effects can be created.

In more advanced applications,
physics calculations may be executing in another process or be
based on the actual amount of time it took to render the last frame.
This is necessary for objects' motions to be independent of frame-rate.

If you take a look at the function, you'll see it first simply
enables or sets different modes depending on several global variables.
This is where lighting, textures, and blending are enabled and
disabled as appropriate. Take a close look at the settings requests
for the filtering; it only affects textures.

Next we're asked to work with the GL_MODELVIEW matrix, which lets us
make transformations that work on the model, or objects, of
our scene. Into this matrix we load the Identity matrix, which is simply
a way of resetting things to the origin, so there's no rotation, translation or
scaling. Lastly, we translate the model out Z_Off distance from the
screen so it can be seen and rotate the model as appropriate.

Let the drawing begin!

Now we're ready to start the actual drawing. (Yippee!) The first
thing we do is clear both the color (frame) buffer and the
depth buffer. It should be pretty obvious why we do this, but it
can be both amusing and enlightening to try not clearing either
or both buffers. Some applications can even guarantee that they'll be
painting the whole screen themselves, so you can save a few cycles by
not clearing the color buffer; the depth buffer must still be cleared,
however.

Figure 2. By turning double-buffering off, you can watch OpenGL draw each frame. Note that OpenGL draws from the bottom up, and each Quad is made up of two triangles.

To start the faces of our cube, we first call glBegin(),
passing GL_QUADS to tell OpenGL that any vertex data received from
this point until the next glEnd() should be interpreted as
four-vertex polygons. Next we call glNormal3f(), passing in
the normal vector for our first polygon. The normal
vector is simply the direction facing right angles to the surface,
and is needed for OpenGL
to be able to calculate lighting correctly. For our application,
all the vertices of each polygon share the same normal, but for curved
surfaces approximated with many polygons, adjusting the normal on a
per-vertex basis can allow the object to appear smooth even though it
isn't.

After setting the normal, we next call glColor4f() with the RGBA settings
for a red face with a 75% level of opaque. Lastly, to complete our
first face, we make four calls to glTexCoord2f() interleaved with
four calls to glVertex3f() to define the position of the texture
on the polygon and the position of the polygon in our 3D world: the floor.

We next do the same type of thing for the top-most grey face, but note
that the texture coordinates are different. The third face rendered,
painted green, also has unusual texture coordinates, but this time they're
non-regular. Compare the code to the visual results, and tweak to see
what kinds of effects can be achieved. Both the grey and green faces are
set to be 50% opaque.

The fourth face, blue, is the first face with the full texture being
requested across its face, and it is adjusted to be only 25% opaque. The
second-to-last face to be drawn demonstrates how OpenGL can smoothly
blend colors across the face of a polygon, ranging from 90% red, green
or blue in three corners to black in the fourth.

The last face, painted yellow, shows that the same kind of blending
applies for the alpha-channel of a polygon. The yellow face ranges
from full transparent at one corner to fully opaque at another.
After we're finished drawing the yellow face, we call
glEnd() to tell OpenGL we're finished drawing Quads (at least, for now).

Drawing text

After drawing the cube, we next want to render some text. However, we
want it to stay on the screen at the same location, and not spin around
with the cube (although that might be neat). To do this, we first
reload the Identity matrix, so we're back to the origin again. Then
we call glMatrixMode() with GL_PROJECTION, which tells OpenGL we
want to work with our projection matrix.

Now, we've already got a nice projection matrix loaded, which
we need to render our rotated cube. However, we also need to reset
this with an orthogonal projection matrix to be able to render in the
true coordinate system of the display. For just this type of
situation, OpenGL lets you save matrices by using the glPushMatrix()
call. After doing this, we can change the projection matrix as needed,
and we can glPopMatrix() the saved matrix back later.

Next, we disable texturing and lighting in case they were activated,
since lit or textured text isn't what we want. The color for the
text is then set, including a 75% level of transparency, just for fun.
Then our code simply renders a text string using sprintf(),
which is then painted on the screen with a call to glRasterPos2i()
and then another to ourPrintString(). The latter is a simple
support function which loops through a string, calling the GLUT library
to render a bitmap for each character as needed. Note that in the
OpenGL system, (0,0) is the bottom-left of the screen.

The next section of code renders the frames-per-second value and
the current frame count in a similar fashion as above. One extra bit
of code first renders a small, mostly opaque dark rectangle for the
FPS value to be rendered onto. This technique is often used to ensure
that some important bit of text can always be seen, regardless of what
might be rendered behind it.

After all the text rendering is complete, we call glPopMatrix() to
recover our saved projection matrix. Then, since we've completed all
the drawing we're going to, we call glutSwapBuffers() to
display our just-drawn image to the user. It's at this point that we
do our motion calculations, and then we make a call to ourDoFPS()
in order to collect the FPS stats. Then we exit the function, returning
control back to OpenGL.

Do that about 50 times a second or so, and you've got a pretty good idea
what's involved in an OpenGL application loop. Obviously, for an application to
be useful (or at least amusing), a great deal more should be going on
in the scene. But what we've covered here is the core basis for most
OpenGL applications.