OpenGL的镶嵌

镶嵌(tessellation)是将凹边形分割或者是凸边形相交边组成的多边形。因为OpenGL只接受凸多边形的渲染,这些非凸多边形必须在绘制前进行镶嵌。

上图分别为凹四边形、中间有洞及自交的五边形。

下载:tessellation.zip,stencilTess.zip

简介

镶嵌的基本过程是将非凸多边形的所有顶点发送至镶嵌器中而不是直接发送至OpenGL渲染管线。然后通过镶嵌器(tessellator)镶嵌多边形。最后,当镶嵌完成,镶嵌器会通过用户定义的回调函例程(callback routine)用OpenGL命令渲染镶嵌多边形。

OpenGL提供大量例程将凹多边形处理为凸多边形:

GLUtessellator* gluNewTess()
void gluDeleteTess(GLUtessellator *tess)

gluNewTess() 创建镶嵌器对象(tesselator object),gluDeleteTess()删除镶嵌器对象。如果创建失败将返回NULL指针。

void gluTessBeginPolygon(GLUtessellator *tess, void *userData)
void gluTessEndPolygon(GLUtessellator *tess)

除了用glBegin()和glEnd()程序块描述多边形的顶点,还可以使用tessellator-specific block, gluTessBeginPolygon()gluTessEndPolygon()。但必须在这个块中描述非凸多边形。

void gluTessBeginContour(GLUtessellator *tess)
void gluTessEndContour(GLUtessellator *tess)

一个多边形可能有超过一个封闭的轮廓(closed contour)(闭环(closed loop)),例如一个多边形有个洞以及2个轮廓,分别为内轮廓和外轮廓。每个轮廓都必须包含在块gluTessBeginContour() 和 gluTessEndContour()中。它是嵌套在gluTessBeginPolygon() 和 gluTessEndPolygon()块中。

void gluTessVertex(GLUtessellator *tess, GLdouble cords[3], void *vertexData)

gluTessVertex()指定轮廓顶点。镶嵌器利用顶点坐标完成镶嵌。所有顶点都大致在相同的平面上。第二个参数是需要镶嵌的顶点坐标,第三个参数用来渲染的数据。它不仅包括顶点坐标,还包括颜色、法线和纹理坐标。

void gluTessCallback(GLUtessellator *tess, GLUenum type, void (*fn)())

在镶嵌步骤中,镶嵌器在准备绘制镶嵌多边形时将会调用一系列的回调函数。必须提供适当的回调函数(callback function),包含OpenGL命令绘制多边形,诸如glBegin(),glEnd(),glVertex*()

下面的代码片段是一般用法的示例。

// 创建镶嵌器
GLUtesselator *tess = gluNewTess();

// 注册回调函数
gluTessCallback(tess, GLU_TESS_BEGIN,   beginCB);
gluTessCallback(tess, GLU_TESS_END,     endCB);
gluTessCallback(tess, GLU_TESS_VERTEX,  vertexCB);
gluTessCallback(tess, GLU_TESS_COMBINE, combineCB);
gluTessCallback(tess, GLU_TESS_ERROR,   errorCB);

//绘制非凸多边形
gluTessBeginPolygon(tess, user_data);
// 第一个轮廓
 gluTessBeginContour(tess);
    gluTessVertex(tess, coords[0], vertex_data);
    ...
 gluTessEndContour(tess);

// 第二个轮廓
gluTessBeginContour(tess);
    gluTessVertex(tess, coords[5], vertex_data);
    ...
gluTessEndContour(tess);
 ...
gluTessEndPolygon(tess);

//在处理后删除镶嵌器
gluDeleteTess(tess);

示例

有三个镶嵌的例子:4个顶点的简单凹边形、含洞多边形、自交多边形(星形图)

下载:tessellation.zip

简单凹边形

tessellate1()描述了这个轮廓的镶嵌程序。在执行镶嵌时,OpenGL镶嵌器是最有效的基本类型;GL_TRIANGLE, GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP 和 GL_LINE_LOOP。这个例子中使用GL_TRIANGLE_FAN镶嵌器三角化。

你可以在执行镶嵌时,记录回调函数中的OpenGL命令。下面的代码是由镶嵌器产生并记录在回调函数中。查看源码中各个回调函数是如何记录的。

glBegin(GL_TRIANGLE_FAN);
    glVertex3dv(v3);
    glVertex3dv(v0);
    glVertex3dv(v1);
    glVertex3dv(v2);
glEnd();

注意这个多边形时逆时针旋转的,也就是说多边形的平面法向量是指向平面外的。如果是顺时针,则法向量是相反方向,你看到的是反面而不是正面。你可以通过使用gluTessNormal()指定平面法线。

void gluTessNormal(GLUtessellator *tess, GLdouble x, GLdouble y, GLdouble z)

如果指定法线向量为(0,0,1),即使是顺时针旋转,你看到的仍然是正面(假设相机观察的是默认方向,-Z)。默认的法线的值是(0,0,0),这意味着镶嵌器将会计算给定坐标和旋绕方向的法线。

含洞多边形

第二个例子是有2个内外都为逆时针的回路。它被定义在tessellate2()中。镶嵌器产生相当于OpenGL命令:1个GL_TRIANGLE_STRIP,1个GL_TRIANGLES

glBegin(GL_TRIANGLE_STRIP);
    glVertex3dv(v1);
    glVertex3dv(v5);
    glVertex3dv(v0);
    glVertex3dv(v4);
    glVertex3dv(v3);
    glVertex3dv(v7);
    glVertex3dv(v2);
    glVertex3dv(v6);
    glVertex3dv(v5);
glEnd();
glBegin(GL_TRIANGLES);
    glVertex3dv(v5);
 glVertex3dv(v1);
    glVertex3dv(v2);
glEnd();

你可能在想OpenGL是怎么知道中间的区域是洞(未填充)。答案是下面的缠绕规律和绕数。 tessellator分配绕数的区域由多个轮廓分割。在这种情况下,有2个独立的区域:内部区域绕数是2,外部区域绕数是1。根据给定的默认绕数规则,GLU_TESS_WINDING_ODD,标有奇数的会被填充,偶数的不填充。

如果内部顺时针,则内部区域绕数为0,外部绕数仍为1。因此,内部区域是个洞(不填充),因为内部区域绕数是0不为奇数。

自交多边形

最后一个例子是星形图,自交多边形,定义在tessellator3()中。注意镶嵌器增加了两两边线相交的5个顶点,分别是v5,v6,v7,v8和v9。当镶嵌算法检测到相交时,然后提供GLU_TESS_COMBINE回调函数创建新的顶点数据,以便我们以后绘制它。

void combineCB(GLdouble newVert[3], GLdouble *neighbourVert[4],
           GLfloat neighborWeight[4], void **outData);

当两条线相交时,回调函数创建新的顶点数据。它包括四个参数:

newVert[3] :类型为GLdouble的x,y,z坐标数组。它存储的是镶嵌器创建的相交顶点的位置信息。第二个参数是4个相邻顶点数组指针,即相交两条线。第三个参数是4个相邻的4个权重值的数组。权重是用来计算交点的颜色、法线和纹理坐标插补。最后一个参数是指向输出顶点数据的指针。输出数据可能不仅包括顶点坐标,还包括颜色、法线和纹理坐标。镶嵌器将会根据绘制的顶点,把输出数据传送到GLU_TESS_VERTEX 回调例程中。

下面是镶嵌器阐述的OpenGL命令;2个三角扇形和1个三角形。

glBegin(GL_TRIANGLE_FAN);
    glVertex3dv(v9);
    glVertex3dv(v0);
    glVertex3dv(v5);
    glVertex3dv(v7);
    glVertex3dv(v8);
    glVertex3dv(v2);
glEnd();
glBegin(GL_TRIANGLE_FAN);
    glVertex3dv(v6);
    glVertex3dv(v1);
    glVertex3dv(v7);
    glVertex3dv(v5);
    glVertex3dv(v3);
glEnd();
glBegin(GL_TRIANGLES);
    glVertex3dv(v4);
    glVertex3dv(v8);
    glVertex3dv(v7);
glEnd();

考虑这个多边形的旋绕规则和绕数。这个多边形将被分为6个独立的区域,绕数如上图所示。

根据GLU_TESS_WINDING_ODD 规则,因为中间区域的绕数为偶数,该区域将不会被填充。我们需要一个不同的旋绕规则,因此我们需要同时填充奇数和偶数区域。这个例子的旋绕规则应该是GLU_TESS_WINDING_NONZERO,允许填充所有非零区域。(GLU_TESS_WINDING_POSITIVE也可以。)

镶嵌器提供gluTessProperty() 来改变旋绕规则和其它属性。例如GLU_TESS_BOUNDARY_ONLYGLU_TESS_TOLERANCE

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

// 示例
gluTessProperty(tess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE);
gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);

旋绕规则和绕数

假设多个轮廓(contours)相互重叠或者嵌套,将平面分为多个区域。旋绕规则确定这些区域是在内部还是外部。所以,内部的需要填充,外部的不需要填充。

多个轮廓将封闭区域划分开来,OpenGL镶嵌器分配到相应区域的绕数。从一个区域中的一个点画一条射线至任意方向的无穷远处。从0开始,如果射线穿过逆时针方向的轮廓路径(从左至右)每次就加1。顺时针方向就减1。在计算所有交叉点后,绕数就代表该区域。

如果旋绕规则是GLU_TESS_WINDING_ODD,奇数的区域将被填充,偶数的不被填充。因此,区域1和-1将被填充,区域0是洞。

旋转规则如下:

GLU_TESS_WINDING_ODD: 奇数填充, 默认设置
GLU_TESS_WINDING_NONZERO: 非零填充
GLU_TESS_WINDING_POSITIVE: 正数填充
GLU_TESS_WINDING_NEGATIVE:负数填充
GLU_TESS_WINDING_ABS_GEQ_TWO: 绝对值大于等于2,则填充

下面的图片显示了基于不同旋转规则的不同内部区域(阴影),如果设置GLU_TESS_WINDING_ABS_GEQ_TWO,将不被绘制(每个区域向外的)。

参考资料

OpenGL Tessellation:http://www.songho.ca/opengl/gltessellation.html#windingrules

时间: 2024-08-11 20:00:02

OpenGL的镶嵌的相关文章

用OpenGL进行曲线、曲面的绘制

实验目的 1)理解Bezier曲线.曲面绘制的基本原理:理解OpenGL中一维.二维插值求值器的用法. 2)掌握OpenGL中曲线.曲面绘图的方法,对比不同参数下的绘图效果差异: 代码1:用四个控制点绘制一条三次Bezier曲线 #include "stdafx.h" #include <stdlib.h> #include <time.h> #include <GL/glut.h> //4个控制点的3D坐标--z坐标全为0 GLfloat ctrl

Mali GPU OpenGL ES 应用性能优化--测试+定位+优化流程

1. 使用DS-5 Streamline定位瓶颈 DS-5 Streamline要求GPU驱动启用性能测试,在Mali GPU驱动中激活性能测试对性能影响微不足道. 1.1 DS-5 Streamline简介 可使用DS-5 Streamline从CPU和Mali GPU中实时收集性能计数器,然后以图形方式显示这些计数器,其主要功能如下:     ? 收集计数器--从CPU和Mali GPU中     ? 保存收集到的计数器数据以供回放     ? 查看显示GPU活动.GPU活动和Framebu

【转】OpenGL相关函数库介绍

原文:http://blog.chinaunix.net/uid-20638550-id-1909182.html OpenGL 函数库相关的API有核心库(gl).实用库(glu).辅助库(aux).实用工具库(glut).窗口库(glx.agl.wgl)和扩展函数库等. 从图1可以看出,gl是核心,glu是对gl的部分封装.glx.agl.wgl 是针对不同窗口系统的函数.glut是为跨平台的OpenGL程序的工具包,比aux功能强大.扩展函数库是硬件厂商为实现硬件更新利用OpenGL的扩

opengl常用函数

glAccum 操作累加缓冲区   glAddSwapHintRectWIN 定义一组被 SwapBuffers拷贝的三角形   glAlphaFunc允许设置alpha检测功能   glAreTexturesResident 决定特定的纹理对象是否常驻在纹理内存中   glArrayElement 定义一个被用于顶点渲染的数组成分   glBegin,glEnd 定义一个或一组原始的顶点   glBindTexture 允许建立一个绑定到目标纹理的有名称的纹理  glBitmap 绘制一个位图

【计算机图形学】openGL常用函数

OpenGL常用函数   glAccum 操作累加缓冲区   glAddSwapHintRectWIN 定义一组被 SwapBuffers拷贝的三角形   glAlphaFunc允许设置alpha检测功能   glAreTexturesResident 决定特定的纹理对象是否常驻在纹理内存中   glArrayElement 定义一个被用于顶点渲染的数组成分   glBegin,glEnd 定义一个或一组原始的顶点   glBindTexture 允许建立一个绑定到目标纹理的有名称的纹理  gl

opengl 函数

( 7 )光栅化.象素操作函数. 像素位置 glRasterPos*() .线型宽度 glLineWidth() .多边形绘制模式 glPolygonMode() ,读取象素 glReadPixel() .复制象素 glCopyPixel() 等. ( 8 )选择与反馈函数. 主要有渲染模式 glRenderMode() .选择缓冲区 glSelectBuffer() 和反馈缓 冲区 glFeedbackBuffer() 等. ( 9 )曲线与曲面的绘制函数. 生成曲线或曲面的函数 glMap*

OpenGL的一些基本概念

首先厘清几个库文件之间的关系,即 gl, glu, glut,glaux 之间的区别 gl glu glut glu32 glut32 glut opengl 后缀为.a 或.lib. 带32,.lib后缀的一般是window平台下的,而.a后缀一般是linux平台下. 以下说明中带*后缀的函数表示该函数有多种版本 下表列出一些后缀字母,它们分别指定了OpenGL的ISO C实现所提供的数据类型 后缀 数据类型 典型的对应C语言类型 OpenGL类型 b 8位整数 singed char GLb

opengl基本库介绍

开发基于OpenGL的应用程序,必须先了解OpenGL的库函数.它采用C语言风格,提供大量的函数来进行图形的处理和显示.OpenGL库函数的命名方式非常有规律.所有OpenGL函数采用了以下格式: <库前缀><根命令><可选的参数个数><可选的参数类型> 库前缀,有gl.glu.aux.glut.wgl.glx.agl等等,分别表示该函数属于OpenGL那个开发库.    从函数名后面中还可以看出需要多少个参数以及参数的类型.I代表int型,f代表float

opengl 实体和网格绘图函数(基础)(转)

http://blog.csdn.net/he_wen_jian/article/details/8594880 GLUT工具箱提供几种图形3维图形的函数: void glutWireSphere(GLdouble radius, GLint slices, GLint stacks);  丝状球void glutSolidSphere(GLdouble radius, GLint slices, GLint stacks); 实心球void glutWireCube(GLdouble size