[openGL]分格化

一、什么叫分格化

分割化就是将非凸多边形分割为凸多边形的过程。

二、为什么要引用分格化

为什么要分格化?因为OpenGL不能渲染非凸多边形;OpenGL为什么不能渲染非凸多边形?因为性能的原因!OpenGL做此限制后,硬件厂商可以只提供快速的多边形渲染硬件来渲符合条件的多边形。

三、如何分格化

glu提供了分析化的函数。如何一个多边形需要分格化,需要执行如下几个典型步骤:

1. 用gluNewTess()函数创建一个新的分析化对象;

2. 调用几次gluTessCallback()函数,注册一些回调函数,在分格化时执行必要的操作。其中,最为复杂的情况是当分格化算法检测到多边形存在相交并且必须调用在GLU_TESS_COMBINE回调函数中所注册的函数的时候。

3. 调用gluTessProperty()函数指定分格化属性。其中最重要属性是环绕规则,它确定了多边形的哪些区域应该被填充,哪些区域不应该着色。

4. 通过指定一个或多个多个闭合多边形的轮廓线来渲染经过分格化的多边形。如果物体的数据是静态的,可以把经过分格化的多边形放在显示列表中;

5. 如果需要对其他物体进行分格化,可以复用原来的分格化对象。如果已经完成了对分格化对象的操作,可以使用gluDeleteTess()函数删除它。

分格化实际上是在把顶点传入到OpenGL管线之前,先将之传到了分格化对象(tessellator),经过分格化后,tessellator再把顶点传入到OpenGL管线,并自动选择合适的图元类型[2]。

四、分格化对象

调用函数:

GLUtesselator *gluNewTess(void);

创建分格化对象,分格化对象包含了分格化操作,并可以向他注册回调函数,在分格化的特定阶段调用。

五、环绕数和环绕规则

当一个“多边形”有洞时,OpenGL如何判断那部分是多边形内部,哪部分是多边形外部?(注意这里内部和外部的意义属于多边形和不属于多边形)。这里就用到环绕数的概念。红宝书上定义:每个点的环绕数就是环绕这个点的所有轮廓线的代数和(用一个有符号整数表示,求和规则是:逆时针环绕的轮廓线加1,顺时针环绕的轮廓线减1)。于是多边形的每个点都有了环绕数。而对于环绕数的解释利用环绕规则来确定,什么样的环绕数叫内部,什么样的环绕数叫外部。

环绕规则用下面的函数指定。

void gluTessProperty(GLUtesselator *tessobj, GLenum property, GLdouble value)

property设为 GLU_TESS_WINDING_RULE,value有五种值 GLU_TESS_WINDING_ODD(默认,奇数为内部), GLU_TESS_WINDING_NONZERO(非0为内部), GLU_TESS_WINDING_POSITVE(正数为内部), GLU_TESS_WINDING_NEGATIVE(负数为内部), GLU_TESS_WINDING_ABS_GEQ_TWO

如何定义逆时针和顺时针,这主依赖于法线,利用右手规则判定。法线用如下函数指定:

void gluTessNormal(GLUtesselator* tessobj, GLdouble x, GLdouble y, GLdouble z);
默认的法线是(0,0,0)。在分格化之前,所有的输入数据都投影到一个与法线垂直的平面上,然后,所有输出三角形的方向都根据法线设置为逆时针方向。

六、分格化回调函数

首先得明白,为什么要引入回调函数。引入回调函数有两方面意义:一是给用户机会查看信息;二是给用户机会修改。回调函数共有十二种,分别在十二种不同的情况下被调用。回调函数的注册调用以下函数:

void gluTessCallback(GLUtesselator *tessobj, GLenum type, void (*fn)());

以在执行分格化的过程中调用回调函数。openGL提供了12种回调函数,每种的参数不同,并且注册,gluTessCallback并没有向fn传递的参数,故而说明,回调函数fn的参数是由openGL自己给的。下面看下这十二种回调函数。

1. GLU_TESS_BEGIN                                                                      void begin(GLenum type);

分格化对象创建后调用,类似glBegin,表示一个图元的开始。给begin传入的参数有四种:GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP, GL_TRIANGLES, GL_LINE_LOOP。具体传入的是哪个参数,是由分格化算法决定的,以哪种图元的效率高为标准。

但是,在begin中除了能知道使用的是哪种图元外,还能干什么呢?指定颜色……

2. GLU_TESS_BEGIN_DATA void begin(GLenum type, void* user_data);

与GLU_TESS_BEGIN相似,只是多传了个参数pointer,这个pointer就是调用glTessBeginPolygon

3. GLU_TESS_VERTEX void vertex(void* vertex_data);

在begin和end之间调用,定义了由分格化过程创建的三角形的顶点。唯一的参数是由gluTessVertex指定的(详见gluTessVertex)。

4. GLU_TESS_VERTEX_DATA

5. GLU_TESS_COMBINE void combine(GLdouble coords[3], void* vertex_data[4], GLfloat weight[4], void **outData);

coords是新顶点的wulf位置(注意只有位置),而顶点的属性是通过vertex_data和weight计算的,vertex_data由gluTessVertex指定顶点坐标时传入。在回调函数combine中通过一系列的计算,产生新的顶点位置和顶点属性(颜色,法线,纹理坐标)存入outData中[3]。

传入combine中的顶点只是由tessellator添加的顶点,而不包括由gluTessVertex指定的顶点,由gluTessVertex指定的顶点还是传入到vertex中。

七、OpenGL 4.0+的Tessellation[4]


参考:

1. 《OpenGL编程指南》

2. http://www.songho.ca/opengl/gl_tessellation.html

3. http://msdn.microsoft.com/en-us/library/windows/desktop/dd374151(v=vs.85).aspx

4. OpenGL 4.0后的Tessellation http://prideout.net/blog/?p=48

你可能感兴趣的:([openGL]分格化)