网格化(tessellation)即把凹多边形,包含洞,岛的多边型,或者自交叉多边型划分为简单凸多边形的过程.

OpenGL为了能快速渲染多边形,只能直接显示简单凸多边形(多边形中任意两点的连线上的所有点仍属于此多边形),对凹多边或者自交叉多边形渲染结果将不确定。在渲染复杂多边形时,常常需要将复杂多边形分解成容易渲染的简单多边形,OpenGL中的使用函数库中提供网格化的函数以通过多边型轮廓线生成简单多边形,从而渲染复杂多边形。

OpenGL进行网格化有5个步骤:

1.使用gluNewTess函数创建一个网格化对象GLUtesselator

2.调用gluTessCallback函数注册网格化期间调用的回调函数。(每个网格化对象需要注册多个回调函数)

3.调用gluTessProperty函数制定网格化的属性,如环绕规则,生成多边形是以填充多边形还是以轮廓的形式绘制,以及合并顶点所需的距离阈值。

4.指定一个或多个封闭多边形轮廓,用于创建分割后的简单多边形集合。

5.可重用网格化对象分割其他对象,在不需要时用gluDeleteTess函数删除网格化对象。(一般情况下使用一个网格化对象即可,多处理器,多线程的情况可使用多个网格化对象,以提高效率)

 

环绕规则用gluTessProperty函数指定:

Void gluTessProperty(GLUtesselator *tessobj, GLenum property, GLdouble value);

gluTessProperty第二个参数使用GLU_TESS_WINDING_RULE

value对应取值:

        GLU_TESS_WINDING_ODD    默认值,渲染环绕数为奇数的区域

        GLU_TESS_WINDING_NONZERO    渲染环绕数不为0的区域

        GLU_TESS_WINDING_POSITIVE   渲染环绕数为正的区域

        GLU_TESS_WINDING_NEGATIVE   渲染环绕数为负的区域

        GLU_TESS_WINDING_ABS_GEQ_TWO    渲染环绕数绝对值大于等于2的区域

(被渲染的区域被称为内部区域)

《OpenGL编程指南》第四版中:“只有一条轮廓线时,每点的环绕数为该绕轮廓一周的过程中经过该点的次数,逆时针为正,顺时针为负。有多条轮廓线时,将分别绕个轮廓线一周得到的环绕数相加。每个点都对应一个有符号的整数值,在同一个区域内,所有点的环绕数都相同。实际这个定义有误,在实际编程中,如对下面程序左下角和右下角的轮廓线而言,此定义是错误的。本人通过实际编程认为环绕数应该针对区域中的点,而不是顶点,在轮廓线之外区域环绕数为0,在计算轮廓线的交点后,轮廓应该类似剥洋葱一样,一层一层的分析,轮廓线逆时针,则内部区域的环绕数+1,顺时针则-1,环绕数应该为计算到最内层轮廓线时各区域得到的值。

环绕规则可用于在计算实体操作时计算轮廓线的并集,差集和交集。

   

以下是演示环绕规则的代码,按p改变环绕规则:

#include <stdio.h>#include <stdlib.h>
#include "gl/glut.h"
 
//注意C++会检查函数指针类型,而C不会
//本程序以.c为扩展名,以.cpp为扩展名gluTessCallback()函数会出现4个错误
 
#ifndef CALLBACK
#define CALLBACK
#endif
 
const char* szCaption = "Tess.cpp";
GLint nProperty=0;                  //环绕规则索引
GLdouble TessProperty[5] = {        //环绕规则
    GLU_TESS_WINDING_ODD,           //环绕数为奇数
    GLU_TESS_WINDING_NONZERO,       //环绕数为非0
    GLU_TESS_WINDING_POSITIVE,      //环绕数为正数
    GLU_TESS_WINDING_NEGATIVE,      //环绕数为负数
    GLU_TESS_WINDING_ABS_GEQ_TWO    //环绕数绝对值大于等于2
};
 
GLdouble graphics0[3][4][3] = {     //左上角图形
    {{10.0, 10.0, 0.0}, {-10.0, 10.0, 0.0}, {-10.0, -10.0, 0.0}, {10.0, -10.0, 0.0}},
    {{7.0, 7.0, 0.0}, {-7.0, 7.0, 0.0}, {-7.0, -7.0, 0.0}, {7.0, -7.0, 0.0}},
    {{4.0, 4.0, 0.0}, {-4.0, 4.0, 0.0}, {-4.0, -4.0, 0.0}, {4.0, -4.0, 0.0}}
};
 
GLdouble graphics1[3][4][3] = {     //右上角图形
    {{10.0, 10.0, 0.0}, {-10.0, 10.0, 0.0}, {-10.0, -10.0, 0.0}, {10.0, -10.0, 0.0}},
    {{7.0, 7.0, 0.0}, {7.0, -7.0, 0.0}, {-7.0, -7.0, 0.0}, {-7.0, 7.0, 0.0}},
    {{4.0, 4.0, 0.0}, {4.0, -4.0, 0.0}, {-4.0, -4.0, 0.0}, {-4.0, 4.0, 0.0}}
};
 
GLdouble graphics2[11][3] = {       //左下角图形
    {10.0, 10.0, 0.0}, {-10.0, 10.0, 0.0}, {-10.0, -10.0, 0.0}, {10.0, -10.0, 0.0},
    {13.0, 2.0, 0.0}, {-13.0, 2.0, 0.0}, {-13.0, -2.0, 0.0}, {13.0, -2.0, 0.0},
    {3.0, 6.0, 0.0}, {-3.0, 6.0, 0.0}, {0.0, -15.0, 0.0}
};
 
GLdouble graphics3[16][3] = {       //右下角图形
    {7.0, 10.0, 0.0}, {-10.0, 10.0, 0.0}, {-10.0, -10.0, 0.0}, {10, -10.0, 0.0},
    {10.0, 1.0, 0.0}, {-1.0, 1.0, 0.0}, {-1.0, -1.0, 0.0}, {1.0, -1.0, 0.0}, 
    {1.0, 4.0, 0.0}, {-4.0, 4.0, 0.0}, {-4.0, -4.0, 0.0}, {4.0, -4.0, 0.0}, 
    {4.0, 7.0, 0.0}, {-7.0, 7.0, 0.0}, {-7.0, -7.0, 0.0}, {7.0, -7.0, 0.0}
};
 
//顶点的回调函数
void CALLBACK vertexCallback(GLvoid* vertex)
{
    GLdouble* pt;
    GLubyte red, green, blue;
    int numb;
    pt = (GLdouble*)vertex;
    numb = rand();
    red = (GLubyte)((numb>>16) & 0xff);
    green = (GLubyte)((numb>>8) & 0xff);
    blue = (GLubyte)(numb & 0xff);
    glColor3ub(red, green, blue);
    glVertex3dv(pt);
}
 
void CALLBACK beginCallback(GLenum type)
{
    glBegin(type);
}
 
void CALLBACK endCallback()
{
    glEnd();
}
 
void CALLBACK errorCallback(GLenum errorCode)
{
    const GLubyte * estring;
    //打印错误类型
    estring = gluErrorString(errorCode);
    fprintf(stderr, "Tessellation Error: %s\n", estring);
    exit(0);
}
 
 
//用于处理检测轮廓线交点,并决定是否合并顶点,
//新创建的顶点最多可以是4个已有顶点线性组合,这些定点坐标存储在data中
//其中weight为权重,weight[i]的总合为1.0
void CALLBACK combineCallback(GLdouble coords[3], 
                             GLdouble* data[4],
                             GLfloat weight[4], GLdouble ** dataout)
{
    GLdouble *vertex;
    int i;
    vertex = (GLdouble*)malloc(6*sizeof(GLdouble));
    vertex[0] = coords[0];
    vertex[1] = coords[1];
    vertex[2] = coords[2];
 
    for(i=3; i<6; i++)      //新顶点的颜色为4个顶点的线型组合
        vertex[i] = weight[0]*data[0][i]+weight[1]*data[1][i]
                    +weight[2]*data[2][i]+weight[3]*data[3][i];
    *dataout = vertex;
}
 
void init()
{
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_SMOOTH);
}
 
void drawGraphics(GLint ngraphic, GLint nproperty)
{
    int i;
    GLUtesselator * tessobj;
    tessobj = gluNewTess();
    //注册回调函数
    gluTessCallback(tessobj, GLU_TESS_VERTEX, vertexCallback);
    gluTessCallback(tessobj, GLU_TESS_BEGIN, beginCallback);
    gluTessCallback(tessobj, GLU_TESS_END, endCallback);
    gluTessCallback(tessobj, GLU_TESS_ERROR, errorCallback);
 
    //设置环绕规则
    gluTessProperty(tessobj, GLU_TESS_WINDING_RULE, TessProperty[nproperty]);
 
    if(2==ngraphic || 3==ngraphic)
        gluTessCallback(tessobj, GLU_TESS_COMBINE, combineCallback);//多边型边自相交的情况下回调用回调函数
    gluTessBeginPolygon(tessobj, NULL);
    switch(ngraphic)    {
    case 0:
        gluTessBeginContour(tessobj);       //定义轮廓线1 逆时针矩形
            gluTessVertex(tessobj, graphics0[0][0], graphics0[0][0]);
            gluTessVertex(tessobj, graphics0[0][1], graphics0[0][1]);
            gluTessVertex(tessobj, graphics0[0][2], graphics0[0][2]);
            gluTessVertex(tessobj, graphics0[0][3], graphics0[0][3]);
        gluTessEndContour(tessobj);
 
        gluTessBeginContour(tessobj);       //定义轮廓线2 逆时针矩形
            gluTessVertex(tessobj, graphics0[1][0], graphics0[1][0]);
            gluTessVertex(tessobj, graphics0[1][1], graphics0[1][1]);
            gluTessVertex(tessobj, graphics0[1][2], graphics0[1][2]);
            gluTessVertex(tessobj, graphics0[1][3], graphics0[1][3]);
        gluTessEndContour(tessobj);
 
        gluTessBeginContour(tessobj);       //定义轮廓线3 逆时针矩形
            gluTessVertex(tessobj, graphics0[2][0], graphics0[2][0]);
            gluTessVertex(tessobj, graphics0[2][1], graphics0[2][1]);
            gluTessVertex(tessobj, graphics0[2][2], graphics0[2][2]);
            gluTessVertex(tessobj, graphics0[2][3], graphics0[2][3]);
        gluTessEndContour(tessobj);
        break;
    case 1:
        gluTessBeginContour(tessobj);       //定义轮廓线1 逆时针矩形
            gluTessVertex(tessobj, graphics1[0][0], graphics1[0][0]);
            gluTessVertex(tessobj, graphics1[0][1], graphics1[0][1]);
            gluTessVertex(tessobj, graphics1[0][2], graphics1[0][2]);
            gluTessVertex(tessobj, graphics1[0][3], graphics1[0][3]);
        gluTessEndContour(tessobj);
 
        gluTessBeginContour(tessobj);       //定义轮廓线2 顺时针矩形
            gluTessVertex(tessobj, graphics1[1][0], graphics1[1][0]);
            gluTessVertex(tessobj, graphics1[1][1], graphics1[1][1]);
            gluTessVertex(tessobj, graphics1[1][2], graphics1[1][2]);
            gluTessVertex(tessobj, graphics1[1][3], graphics1[1][3]);
        gluTessEndContour(tessobj);
 
        gluTessBeginContour(tessobj);       //定义轮廓线3 顺时针矩形
            gluTessVertex(tessobj, graphics1[2][0], graphics1[2][0]);
            gluTessVertex(tessobj, graphics1[2][1], graphics1[2][1]);
            gluTessVertex(tessobj, graphics1[2][2], graphics1[2][2]);
            gluTessVertex(tessobj, graphics1[2][3], graphics1[2][3]);
        gluTessEndContour(tessobj);
        break;
    case 2:
        gluTessBeginContour(tessobj);       //定义轮廓线1 逆时针矩形
            gluTessVertex(tessobj, graphics2[0], graphics2[0]);
            gluTessVertex(tessobj, graphics2[1], graphics2[1]);
            gluTessVertex(tessobj, graphics2[2], graphics2[2]);
            gluTessVertex(tessobj, graphics2[3], graphics2[3]);
        gluTessEndContour(tessobj);
 
        gluTessBeginContour(tessobj);       //定义轮廓线2 逆时针矩形
            gluTessVertex(tessobj, graphics2[4], graphics2[4]);
            gluTessVertex(tessobj, graphics2[5], graphics2[5]);
            gluTessVertex(tessobj, graphics2[6], graphics2[6]);
            gluTessVertex(tessobj, graphics2[7], graphics2[7]);
        gluTessEndContour(tessobj);
 
        gluTessBeginContour(tessobj);       //定义轮廓线3 逆时针三角形
            gluTessVertex(tessobj, graphics2[8], graphics2[8]);
            gluTessVertex(tessobj, graphics2[9], graphics2[9]);
            gluTessVertex(tessobj, graphics2[10], graphics2[10]);
        gluTessEndContour(tessobj);
        break;
    case 3:
        gluTessBeginContour(tessobj);
            for(i=0; i<16; i++)
                gluTessVertex(tessobj, graphics3[i], graphics3[i]);
        gluTessEndContour(tessobj);
        break;
    default:
        break;
    }
    gluTessEndPolygon(tessobj);
}
 
void reshape(GLsizei w, GLsizei h)
{
    if(0 == h)
        h=1;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if(w>h)
        gluPerspective(40.0, (GLfloat)w/(GLfloat)h, 0.1, 100.0);
    else
        gluPerspective(40.0*(GLfloat)h/(GLfloat)w, (GLfloat)h/(GLfloat)w, 0.1, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
 
void display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
        glTranslated(-8.0, 8.0, -45.0);
        glScaled(0.5, 0.5, 1.0);
        drawGraphics(0, nProperty);
    glPopMatrix();
 
    glPushMatrix();
        glTranslated(8.0, 8.0, -45.0);
        glScaled(0.5, 0.5, 1.0);
        drawGraphics(1, nProperty);
    glPopMatrix();
 
    glPushMatrix();
        glTranslated(-8.0, -8.0, -45.0);
        glScaled(0.5, 0.5, 1.0);
        drawGraphics(2, nProperty);
    glPopMatrix();
 
    glPushMatrix();
        glTranslated(8.0, -8.0, -45.0);
        glScaled(0.5, 0.5, 1.0);
        drawGraphics(3, nProperty);
    glPopMatrix();
    glFlush();
}
 
void keyboard(GLubyte key, GLint x, GLint y)
{
    switch(key) {
    case 'p':
    case 'P':
        nProperty++;
        nProperty = nProperty%5;
        glutPostRedisplay();
        break;
    case 27:
        exit(0);
        break;
    default:
        break;
    }
}
 
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(400, 400);
    glutInitWindowPosition(0, 0);
    glutCreateWindow(szCaption);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutMainLoop();
    return 0;
}