OpenGL系列教程之十一:OpenGL网格化
網(wǎng)格化是將凹多邊形或有邊相交的多邊形劃分成凸多邊形。由于openGL渲染時只接受凸多邊形,這些非凸多邊形在渲染之前必須先被網(wǎng)格化。
第一行中第一個圖形是4條邊的凹多邊形,第二個圖形中間有個洞,第三個圖形有相交的邊
下載:?tessellation.zip,?stencilTess.zip
概述
網(wǎng)格化基本的步驟是將所有非凸多邊形的頂點(diǎn)坐標(biāo)發(fā)送到網(wǎng)格器而不是直接發(fā)送到OpenGL渲染管線中,然后網(wǎng)格器將所有多邊形網(wǎng)格化。最后,網(wǎng)格化工作完成以后,網(wǎng)格器使用用戶定義的回調(diào)模式調(diào)用實(shí)際的OpenGL命令來渲染網(wǎng)格化后的多邊形。
OpenGL提供了一系列的將凹多邊形處理成凸多邊形的模式。
[cpp]?view plaincopy
[cpp]?view plaincopy
不像之前使用glBegin()和glEnd()塊來描述多邊形的頂點(diǎn),你需要使用特殊的網(wǎng)格器塊,gluTessBeginPolygon()和gluTessEndPolygon().你必須在這個塊中描述非凸多邊形。
[cpp]?view plaincopy
一個多邊形可能有多個封閉的輪廓線(封閉的回路),例如,一個有洞的多邊形有2條回路,里面一條外面一條。每一個回路必須被gluTessBeginContour()和gluTessEndContour()包含。這是在gluTessBeginPolygon()和gluTessEndPolygon()中內(nèi)嵌的塊。
[cpp]?view plaincopy
在網(wǎng)格化的過程中,當(dāng)OpenGL準(zhǔn)備渲染網(wǎng)格化后的多邊形形時網(wǎng)格器會調(diào)用一系列的回調(diào)模式。你必須指定適當(dāng)?shù)幕卣{(diào)函數(shù),這些回調(diào)函數(shù)包括實(shí)際渲染多邊形的OpenGL命令,如glBegin(),glEnd(),glVertex*()等。
下面一小段代碼顯示了網(wǎng)格化的使用方法:
[cpp]?view plaincopy
例子:
上圖中有3個網(wǎng)格器的例子,左邊是一個簡單的有4個頂點(diǎn)的凹多邊形,中間是一個有洞的多邊形,右邊是一個有相交邊的多邊形(星形)。
下載源文件和可執(zhí)行文件:tessellation.zip
一個簡單的凹多邊形的例子
凹多邊形的網(wǎng)格化
這個輪廓的網(wǎng)格化模式定義在tessellate1()函數(shù)中。OpenGL網(wǎng)格器定義和多種基本的圖元類型來高效地執(zhí)行網(wǎng)格化:GL_TRIANGLE, GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP and GL_LINE_LOOP。本例中,網(wǎng)格器是使用GL_TRIANGLE_FAN將多邊形劃分成三角形。
你可以將實(shí)際的OpenGL記錄在回調(diào)函數(shù)中,這些函數(shù)在網(wǎng)格化的過程中會被執(zhí)行。下面的代碼是網(wǎng)格器生成的并記錄在回調(diào)函數(shù)中。在源文件中查看回調(diào)函數(shù)式如何記錄的。
[cpp]?view plaincopy
注意多邊形的環(huán)繞方式是逆時針的(CCW),這樣多邊形的表面法向量是指離多邊形的。如果環(huán)繞方式是順時鐘的(CW),表面法向量則是指向多邊形的,這樣你看到的是多邊形的背面而不是前面。你可以使用gluTessNormal()來明確地指明表面法向量。
[cpp]?view plaincopy
如果你用(0,0,1)指定了法向量,那么你將一直看到多邊形的前面即使環(huán)繞方式是順時針的(我假設(shè)照相機(jī)是指向默認(rèn)的方向,即-Z軸)。默認(rèn)的法向量的值是(0,0,0),這意味著網(wǎng)格器會更加給定的頂點(diǎn)和環(huán)繞方式計算法向量。
有洞的多邊形
第二個例子有兩個逆時針的回路,里面一個外面一個。它被定義在tessellate2()中。網(wǎng)格器一個三角形一個三角形地生成實(shí)際的OpenGL命令。
[cpp]?view plaincopy
你可能會疑惑OpenGL網(wǎng)格器是怎么知道中間的區(qū)域是洞的(不需要填充)。答案是環(huán)繞的規(guī)則和環(huán)繞的數(shù)字。網(wǎng)格器給被回路劃分的多個區(qū)域分配了環(huán)繞的數(shù)字。在本例中,有2個單獨(dú)的區(qū)域:里面區(qū)域被分配的環(huán)繞數(shù)字是2,外面區(qū)域是1。使用默認(rèn)的環(huán)繞規(guī)則,GLU_TESS_WINDING_ODD,被奇數(shù)標(biāo)記的區(qū)域會被填充,被偶數(shù)標(biāo)記的區(qū)域則不會被填充。
如果內(nèi)部的輪廓是順時針方向的,那么現(xiàn)在內(nèi)部區(qū)域的環(huán)繞數(shù)字是0,外面的是1。因此,內(nèi)部的區(qū)域依然是一個洞(不填充)因?yàn)閮?nèi)部區(qū)域的環(huán)繞數(shù)字不是偶數(shù),而是0。環(huán)繞的規(guī)則在下面會講到。
有邊相交的多邊形
最后一個例子是一個星型,它有邊相交并被定義在tessellator3()。注意網(wǎng)格器添加了5個額外的頂點(diǎn)并插入了2條邊,v5,v6,v7,v8和v9。當(dāng)網(wǎng)格器算法檢測到插入邊時。GLU_TESS_COMBINE回調(diào)函數(shù)必須被提供來創(chuàng)建新的頂點(diǎn),我們稍后會繪制它。
[cpp]?view plaincopy
當(dāng)兩條邊插入時回調(diào)函數(shù)用來創(chuàng)建新的頂點(diǎn)。它需要4個參數(shù):newVert[3]是x,y,z坐標(biāo)的一個數(shù)組,這是網(wǎng)格器檢測到的新插入的頂點(diǎn)的坐標(biāo)。第二個參數(shù)指向4個鄰接頂點(diǎn)的坐標(biāo)的指針。第3個參數(shù)是4個鄰接頂點(diǎn)的權(quán)重因素。這個權(quán)重值會被用來計算插入點(diǎn)的顏色,法向量,或者紋理坐標(biāo)。最后一個參數(shù)是指向輸出頂點(diǎn)數(shù)據(jù)的指針。輸出數(shù)據(jù)可能不止包含頂點(diǎn)坐標(biāo),也包含顏色,法向量,紋理坐標(biāo)。玩?zhèn)€器將會將輸出數(shù)據(jù)傳送到GLU_TESS_VERTE回調(diào)模式中來繪制插入的頂點(diǎn)數(shù)據(jù)。
下面的OpenGL命令是使用網(wǎng)格器生成的:
[cpp]?view plaincopy
在這個多邊形中考慮環(huán)繞規(guī)則和環(huán)繞數(shù)字。多邊形被分成了6個單獨(dú)的區(qū)域,環(huán)繞的數(shù)字如圖所示:
使用GLU_TESS_WINDING_ODD規(guī)則,中間區(qū)域不會被填充因?yàn)樗沫h(huán)繞數(shù)字是偶數(shù)。我們需要另外一個環(huán)繞規(guī)則,這樣我們可以填充環(huán)繞數(shù)字為奇數(shù)和偶數(shù)的區(qū)域。這種情況下則應(yīng)該使用GLU_TESS_WINDING_NODZERO環(huán)繞規(guī)則,它會填充環(huán)繞數(shù)字非0的區(qū)域。(GLU_TESS_WINDING_POSITIVE同時也工作)
網(wǎng)格器提供gluTessProerty()函數(shù)改變環(huán)繞規(guī)則和其他屬性,例如GLU_TESS_BOUNDARY_ONLY,glu_TESS_TOLERANCE。更多環(huán)繞規(guī)則的內(nèi)容查看下面。
環(huán)繞規(guī)則和環(huán)繞數(shù)字
假設(shè)多個輪廓線,相互之間重疊或嵌套,將平面劃分成了多個區(qū)域。環(huán)繞規(guī)則決定了哪些區(qū)域是里面還是外面。這樣里面會被填充,外面不會被填充。
對每個被多條輪廓線封閉的區(qū)域,OpenGL網(wǎng)格器給這個區(qū)域分配了一個環(huán)繞數(shù)字。從一個區(qū)域內(nèi)一點(diǎn)向各個方向發(fā)射一條射線。從0開始計數(shù),如果這條射線與逆時針的輪廓線相交則加1,與順時針的輪廓線相交則減1。計數(shù)完所有相交線后,最后環(huán)繞的數(shù)字表示那個區(qū)域。
如果環(huán)繞的規(guī)則是GLU_TESS_WINDING_ODD,環(huán)繞數(shù)字是奇數(shù)的區(qū)域是里面并被填充,環(huán)繞數(shù)字是偶數(shù)的區(qū)域是外面并不被填充。因此區(qū)域1和-1會被填充,區(qū)域0是一個洞。
可能的環(huán)繞規(guī)則如下:
GLU_TESS_WINDING_ODD: 填充奇數(shù),默認(rèn)的設(shè)置
GLU_TESS_WINDING_NONZERO: 填充非零區(qū)域
GLU_TESS_WINDING_POSITIVE: 填充正數(shù)區(qū)域
GLU_TESS_WINDING_NEGATIVE: 填充負(fù)數(shù)區(qū)域
GLU_TESS_WINDING_ABS_GEQ_TWO: 填充絕對值大于或等于2的區(qū)域
下面的圖像顯示了不同環(huán)繞規(guī)則下定義的不同的里面。如果GLU_TESS_WINDING_ABS_GEQ_TWO別設(shè)置,那么什么都不會繪制(所有的區(qū)域都是外面)。
總結(jié)
以上是生活随笔為你收集整理的OpenGL系列教程之十一:OpenGL网格化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Flutter 使用环信即时通讯闪退解决
- 下一篇: 为什么分布式一定要有 Redis?(转自