Rendering Grids with OpenGL Evaluators
I often hear requests from OpenGL
programmers asking how to quickly render a graph paper-like grid.
Grids are often used as backdrops for 3D models to get extra distance
cues to the viewer. Often grids are Cartesian, but parametric grids are
also useful.
The simplest way to draw a Cartesian grid is as a set of horizontal and
vertical unconnected lines:
/* Render grid over 0..rows, 0..columns. */
glBegin(GL_LINES);
/* Horizontal lines. */
for (i=0; i<=rows; i++) {
glVertex2f(0, i);
glVertex2f(columns, i);
}
/* Vertical lines. */
for (i=0; i<=columns; i++) {
glVertex2f(i, 0);
glVertex2f(i, rows);
}
glEnd();
Fancier grids may be rotated, sheared, or stretched. Even fancier grids
are actually surface meshes defined by control points that describe a
Bezier surface patch. OpenGL has a standard feature called evaluators
that sets up a polynomial or rational polynomial mapping for producing
vertex, normal, texture coordinates, and colors. With evaluators, it is
actually quite straightforward to generate very fancy grids.
An Example: Rendering a Grid with Evaluators
Say you have the four corners of a grid. The corners of the grid can
actually be thought of as control points for a linear (2nd order, ie.
very simple) Bezier patch. It is not all that hard to setup OpenGL
using evaluators to draw the grid in a few short commands.
Say our grid corners are located at (-2,-2), (4,-2), (-2,3), and (4,3).
First set up an array of these coordinates (as 3D coordinates):
GLfloat grid2x2[2][2][3] = {
{{-2.0, -2.0, 0.0}, {4.0, -2.0, 0.0}},
{{-2.0, 3.0, 0.0}, {3.0, 4.0, 0.0}}
};
Before rendering the grid, we enabled the GL_MAP2_VERTEX_3
evaluator map and pass the control points to OpenGL with the glMap2f
command:
glEnable(GL_MAP2_VERTEX_3);
glMap2f(GL_MAP2_VERTEX_3,
0.0, 1.0, /* U ranges 0..1 */
3, /* U stride, 3 floats per coord */
2, /* U is 2nd order, ie. linear */
0.0, 1.0, /* V ranges 0..1 */
2 * 3, /* V stride, row is 2 coords, 3 floats per coord */
2, /* V is 2nd order, ie linear */
grid2x2); /* control points */
When we actually go to evaluate and render the evaluator map as a 2D
mesh, we need to indicate the number of partitions of U and V (number
of grid rows and columns) and over what region of the parametric U and
V domain we will iterate across. So we tell OpenGL to iterate across
the full 0.0 to 1.0 range setup above with 5 rows and 6 columns. This
is done with glMapGrid2f:
glMapGrid2f(
5, 0.0, 1.0,
6, 0.0, 1.0);
After this setup is performed, a single OpenGL command evaluates and
renders the specified grid as an evaluator mesh:
glEvalMesh2(GL_LINE,
0, 5, /* Starting at 0 mesh 5 steps (rows). */
0, 6); /* Starting at 0 mesh 6 steps (columns). */
That may seem like a ton of setup work just to render a grid. With all
the parameters required, it should not be at all surprising that
OpenGL's evaluator mechanism can do a lot more than render simple
grids. Along with grid outlines (GL_LINE), evaluators can
generate solid patches (GL_FILL). OpenGL can also
automatically generate normals. Colors and texture coordinates can also
be evaluated.
Even with all the generality, why would an OpenGL programmer go through
all the bother of specifying evaluator parameters? Couldn't an
application just calculate the vertices for fancy Bezier patches and
then just render the result with normal OpenGL primitives? Yes, but
parametric curves and surfaces are both common (particularly in CAD and
3D modeling applications) and quite computationally expensive. It makes
sense for OpenGL to off-load the floating-point intensive task of
evaluating these curves and surfaces to fast 3D transformation engines
when available. Indeed, SGI's RealityEngine, InfiniteReality, IMPACT,
and Octane workstations all off-load OpenGL evaluator calculations to
the dedicated Geometry Engine graphics hardware.
I describe the really really simple case of rendering a grid with
evaluators not because it is a particularly wonderful or
straightforward way to render grids with OpenGL, but because it helps
introduce the concept of OpenGL evaluators without getting too
complicated too fast. A grid is a nice, simple case of a flat Bezier
surface.
A Complete Example Program
To further help you appreciate how a grid can be defined by control
points, I've written a simple GLUT-based
OpenGL program called editgrid. Check out the source file editgrid.c The program allows you to
interactively move the control points for a simple 2D grid. For example: