OpenGL itself is only capable of directly rendering convex polygons such as triangles, triangle fans, etc. More complex objects, through tessellation, must be subdivided into these simple convex polygons before they can be displayed. Luckily for us, the GLU (OpenGL Utility Library) contains an easy solution for polygon tessellation of concave polygons, polygons with holes, or polygons whose sides intersect. Basically, the GLU routines are fed a bunch of complicated vertex data that make up the contours of a complex polygon, which are then calculated and spit out as neat, bite-size convex polygons that OpenGL will have no problem displaying.
A polygon is a many-sided planar figure composed of vertices and edges.
Vertices are represented by points (x,y).
Edges are represented as line segments which connect two points, (x1,y1) and (x2,y2).
Convex Polygon - For any two points P1, P2 inside the polygon, all points on the
line segment which connects P1 and P2 are inside the polygon.
All points P = uP1 + (1-u)P2, u in [0,1] are inside the polygon provided that P1
and P2 are inside the polygon.
Concave Polygon - A polygon which is not convex.
The first thing we need to do is to use the gluNewTess(void) routine to create a new tessellation
object, the basis for all the work we'll do, and return a GLUtesselator* pointer to it. It's possible to create
more than one tessellation object, perhaps to have different objects with different sets of properties or callback
functions, but most applications reuse the same one for different polygons and change properties for the single
tessellation object when needed.
Once the tessellation object is created, callback functions for that object have to be specified by using the gluTessCallback(
) routine. These callback functions will be called by GLU in certain instances during the tessellation
of our polygons. Such instances may include when a vertex has to be drawn, or when two sides of a polygon intersect
and a new vertex has to be calculated.
After we specify the callback functions, we have to specify tessellation properties with the gluTessProperty( ) routine. For tessellation objects, property can be GLU_TESS_BOUNDARY_ONLY, GLU_TESS_TOLERANCE, or GLU_TESS_WINDING_RULE.
The winding rule defines which contours of a polygon are "interior," or "filled," and which
are "exterior," or "cut out," and are especially important when tessellating polygons with
holes. A winding number is assigned to every contour of a polygon. Click here for
a diagram from The OpenGL Programming Guide that is great for showing how the winding rule can significantly affect
the tessellation of polygons.
Finally, we can now begin polygon definition. These two routines begin and end a polygon, and surround the definition
of one or more contours:
void gluTessBeginPolygon(GLUtesselator *tessobj, void *user_data);
void gluTessEndPolygon(GLUtesselator *tessobj);
These two routines begin and end a single contour, and go inside a polygon's definition:
void gluTessBeginContour(GLUtesselator *tessobj);
void gluTessEndContour(GLUtesselator *tessobj);
Within a contour's definition, this routine is called to specify and store a vertex for the current contour:
void gluTessVertex(GLUtesselator *tessobj, Gldouble coords[3], void *vertex_data);
After we're done with all the polygon tessellations we need, we can destroy the tessellation object by making
a call to:
void gluDeleteTess(GLUtesselator *tessobj);
The following example draws the letter "A" and exercises the winding rules (keys 1 -> 5).
#include <GL/glut.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
GLfloat rotation = 0.0;
int windingRule = 1;
char s[100];
void changeSize(GLsizei width, GLsizei height) {
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(9.0, (float) width / (float) height, 0.1, 1000.0);
glMatrixMode(GL_MODELVIEW);
}
void renderScene() {
GLUtriangulatorObj * tess;
static GLdouble outside[7][3] =
{ { 0.0, 1.0, 0.0 }, { -0.5, -1.0, 0.0 }, { -0.4, -1.0, 0.0 }, { -0.2, -0.1, 0.0 },
{ 0.2, -0.1, 0.0 }, { 0.4, -1.0, 0.0 }, { 0.5, -1.0, 0.0 } };
static GLdouble inside[3][3] =
{ { 0.0, 0.6, 0.0 },{ -0.1, 0.1, 0.0 },{ 0.1, 0.1, 0.0 } };
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); //no transformation
glTranslatef(0.0, 0.0, -15.0);
glRotatef(rotation, 0.0, 1.0, 0.0);
glColor3f(0.0, 0.0, 1.0);
tess = gluNewTess();
gluTessCallback(tess, GLU_BEGIN, (GLvoid (__stdcall *) ( )) glBegin);
gluTessCallback(tess, GLU_VERTEX, (GLvoid (__stdcall *) ( )) glVertex3dv);
gluTessCallback(tess, GLU_END, (GLvoid (__stdcall *) ( )) glEnd);
switch (windingRule) {
case 1 : gluTessProperty(tess, GLU_TESS_WINDING_RULE,GLU_TESS_WINDING_ODD); break;
case 2 : gluTessProperty(tess, GLU_TESS_WINDING_RULE,GLU_TESS_WINDING_NONZERO); break;
case 3 : gluTessProperty(tess, GLU_TESS_WINDING_RULE,GLU_TESS_WINDING_POSITIVE); break;
case 4 : gluTessProperty(tess, GLU_TESS_WINDING_RULE,GLU_TESS_WINDING_NEGATIVE); break;
case 5 : gluTessProperty(tess, GLU_TESS_WINDING_RULE,GLU_TESS_WINDING_ABS_GEQ_TWO); break;
}
gluBeginPolygon(tess);
gluTessVertex(tess, outside[0], outside[0]);
gluTessVertex(tess, outside[1], outside[1]);
gluTessVertex(tess, outside[2], outside[2]);
gluTessVertex(tess, outside[3], outside[3]);
gluTessVertex(tess, outside[4], outside[4]);
gluTessVertex(tess, outside[5], outside[5]);
gluTessVertex(tess, outside[6], outside[6]);
gluNextContour(tess, GLU_INTERIOR);
gluTessVertex(tess, inside[0], inside[0]);
gluTessVertex(tess, inside[1], inside[1]);
gluTessVertex(tess, inside[2], inside[2]);
gluEndPolygon(tess);
gluDeleteTess(tess);
glPopMatrix();
glutSwapBuffers();;
}
void rotateObject() {
rotation += 1.0;
if (rotation >= 360.0) rotation -= 360.0;
renderScene();
}
void inputKey(unsigned char c, int x, int y) {
switch (c) {
case '1' : sprintf(s,"GLU_TESS_WINDING_ODD"); break;
case '2' : sprintf(s,"GLU_TESS_WINDING_NONZERO"); break;
case '3' : sprintf(s,"GLU_TESS_WINDING_POSITIVE"); break;
case '4' : sprintf(s,"GLU_TESS_WINDING_NEGATIVE"); break;
case '5' : sprintf(s,"GLU_TESS_WINDING_ABS_GEQ_TWO"); break;
default: sprintf(s,"GLU_TESS_WINDING_ODD"); c = '1'; break;
}
windingRule = c - 48;
glutSetWindowTitle(s);
}
void main(int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB);
glutInitWindowPosition(100,100);
glutInitWindowSize(300,200);
glutCreateWindow("The Letter A");
glutDisplayFunc(renderScene);
glutKeyboardFunc(inputKey);
glutReshapeFunc(changeSize);
glutIdleFunc(rotateObject);
glutMainLoop();
}
![]() |
Example Code |