4 GLU

4.010 What is GLU? How is it different from OpenGL?

If you think of OpenGL as a low-level 3D graphics library, think of GLU as adding some higher-level functionality not provided by OpenGL. Some of GLU's features include:

  • Scaling of 2D images and creation of mipmap pyramids
  • Transformation of object coordinates into device coordinates and vice versa
  • Support for NURBS surfaces
  • Support for tessellation of concave or bow tie polygonal primitives
  • Specialty transformation matrices for creating perspective and orthographic projections, positioning a camera, and selection/picking
  • Rendering of disk, cylinder, and sphere primitives
  • Interpreting OpenGL error values as ASCII text

The best source of information on GLU is the OpenGL red and blue books and the GLU specification, which you can obtain from the OpenGL org Web page.

4.020 How does GLU render sphere, cylinder, and disk primitives?

There is nothing special about how GLU generates these primitives. You can easily write routines that do what GLU does. You can also download the Mesa source, which contains a GLU distribution, and see what these routines are doing.

The GLU routines approximate the specified primitive using normal OpenGL primitives, such as quad strips and triangle fans. The surface is approximated according to user parameters. The vertices are generated using calls to the sinf() and cosf() math library functions.

4.030 How does gluPickMatrix() work?

It simply translates and scales so that the specified pick region fills the viewport. When specified on the projection matrix stack, prior to multiplying on a normal projection matrix (such as gluPerspective(), glFrustum(), glOrtho(), or gluOrtho2D()), the result is that the view volume is constrained to the pick region. This way only primitives that intersect the pick region will fall into the view volume. When glRenderMode() is set to GL_SELECT, these primitives will be returned.

4.040 How do I use GLU tessellation routines?

GLU provides tessellation routines to let you render concave polygons, self-intersecting polygons, and polygons with holes. The tessellation routines break these complex primitives up into (possibly groups of) simpler, convex primitives that can be rendered by the OpenGL API. This is done by providing the data of the simpler primitives to your application from callback routines that your application must provide. Your app can then send the data to OpenGL using normal API calls.

An example program is available in the GLUT distribution under progs/redbook/tess.c. (Download the GLUT distribution).

The usual steps for using tessellation routines are:

1. Allocate a new GLU tessellation object:

GLUtesselator *tess = gluNewTess();

2. Assign callbacks for use with this tessellation object:

gluTessCallback (tess, GLU_TESS_BEGIN, tcbBegin);
gluTessCallback (tess, GLU_TESS_VERTEX, tcbVertex);
gluTessCallback (tess, GLU_TESS_END, tcbEnd);

2a. If your primitive is self-intersecting, you must also specify a callback to create new vertices:

gluTessCallback (tess, GLU_TESS_COMBINE, tcbCombine);

3. Send the complex primitive data to GLU:

// Assumes:
//    GLdouble data[numVerts][3];
// …and assumes the array has been filled with 3D vertex data.

gluTessBeginPolygon (tess, NULL);
gluTessBeginContour (tess);
for (i=0; i<sizeof(data)/(sizeof(GLdouble)*3);i++)
   gluTessVertex (tess, data[i], data[i]);
gluTessEndContour (tess);
gluEndPolygon (tess);

4. In your callback routines, make the appropriate OpenGL calls:

void tcbBegin (GLenum prim);
{
   glBegin (prim);
}

void tcbVertex (void *data)
{
   glVertex3dv ((GLdouble *)data);
}

void tcbEnd ();
{
   glEnd ();
}

void tcbCombine (GLdouble c[3], void *d[4], GLfloat w[4], void **out)
{
   GLdouble *nv = (GLdouble *) malloc(sizeof(GLdouble)*3);

   nv[0] = c[0];
   nv[1] = c[1];
   nv[2] = c[2];
   *out = nv; 
}

The above list of steps and code segments is a bare-bones example and is not intended to demonstrate the full capabilities of the tessellation routines. By providing application-specific data as parameters to gluTessBeginPolygon() and gluTessVertex() and handling the data in the appropriate callback routines, your application can color and texture map these primitives as it would a normal OpenGL primitive.

4.050 Why aren't my tessellation callback routines called?

Normally your tessellation callback routines are executed when you call gluEndPolygon(). If they are not being called, an error has occurred. Typically this is caused when you haven't defined a GLU_TESS_COMBINE* callback for a self-intersecting primitive.

You might try defining a callback for GLU_TESS_ERROR to see if it's called.

4.060 How do I use GLU NURBS routines?

The GLU NURBS interface converts the B-Spline basis control points into Bezier basis equivalents and calls directly to the OpenGL Evaluator routines to render the surface.

An example program is available in the GLUT distribution under progs/redbook/surface.c. (Download the GLUT distribution).

4.070 How do I use gluProject() and gluUnProject()?

Both routines take a ModelView matrix, Projection matrix, and OpenGL Viewport as parameters.

gluProject() also takes an XYZ-object space coordinate. It returns the transformed XYZ window (or device) coordinate equivalent.

gluUnProject() does the opposite. It takes an XYZ window coordinate and returns the back-transformed XYZ object coordinate equivalent.

The concept of window space Z is often confusing. It's the depth buffer value expressed as a GLdouble in the range 0.0 to 1.0. Assuming a default glDepthRange(), a window coordinate with a Z value of 0.0 corresponds to an eye coordinate located on the zNear clipping plane. Similarly, a window space Z value of 1.0 corresponds to an eye space coordinate located on the zFar plane. You can obtain any window space Z value by reading the depth buffer with glReadPixels().