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