日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OpenGL系列教程之十一:OpenGL网格化

發(fā)布時間:2024/3/24 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenGL系列教程之十一:OpenGL网格化 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

網(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
  • GLUtessellator*?gluNewTess()??
  • ??
  • void?gluDeleteTess(GLUtessellator?*tess)??

  • gluNewTess()創(chuàng)建一個網(wǎng)格器對象,gluDeleteTess()刪除指定的網(wǎng)格器對象。如果創(chuàng)建失敗,將會返回NULL。

    [cpp]?view plaincopy
  • void?gluTessBeginPolygon(GLUtessellator?*tess,?void?*userData)??
  • ??
  • void?gluTessEndPolygon(GLUtessellator?*tess)??

  • 不像之前使用glBegin()和glEnd()塊來描述多邊形的頂點(diǎn),你需要使用特殊的網(wǎng)格器塊,gluTessBeginPolygon()和gluTessEndPolygon().你必須在這個塊中描述非凸多邊形。

    [cpp]?view plaincopy
  • void?gluTessBeginContour(GLUtessellator?*tess)??
  • ??
  • void?gluTessEndContour(GLUtessellator?*tess)??

  • 一個多邊形可能有多個封閉的輪廓線(封閉的回路),例如,一個有洞的多邊形有2條回路,里面一條外面一條。每一個回路必須被gluTessBeginContour()和gluTessEndContour()包含。這是在gluTessBeginPolygon()和gluTessEndPolygon()中內(nèi)嵌的塊。


    [cpp]?view plaincopy
  • void?gluTessVertex(GLUtessellator?*tess,?GLdouble?cords[3],?void?*vertexData)??

  • gluTessVertex()指定了回路的頂點(diǎn)。網(wǎng)格器使用這些頂點(diǎn)坐標(biāo)執(zhí)行網(wǎng)格化。所有的頂點(diǎn)需要位于同一個面中。第二個參數(shù)是網(wǎng)格化需要的頂點(diǎn)坐標(biāo),第三個參數(shù)是實(shí)際用來渲染的坐標(biāo),它可能不僅是頂點(diǎn)坐標(biāo),也可能是顏色坐標(biāo),法向量坐標(biāo),紋理坐標(biāo)。

    [cpp]?view plaincopy
  • void?gluTessCallback(GLUtessellator?*tess,?GLUenum?type,?void?(*fn)())??

  • 在網(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
  • //?創(chuàng)建網(wǎng)格器??
  • GLUtesselator?*tess?=?gluNewTess();??
  • ??
  • //?注冊回調(diào)函數(shù)??
  • 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);??
  • ??
  • //?描述非凸多邊形的頂點(diǎn)??
  • 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);??
  • ??
  • //?處理完成后刪除網(wǎng)格器??
  • gluDeleteTess(tess);??





  • 例子:


    上圖中有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
  • glBegin(GL_TRIANGLE_FAN);??
  • ????glVertex3dv(v3);??
  • ????glVertex3dv(v0);??
  • ????glVertex3dv(v1);??
  • ????glVertex3dv(v2);??
  • glEnd();??

  • 注意多邊形的環(huán)繞方式是逆時針的(CCW),這樣多邊形的表面法向量是指離多邊形的。如果環(huán)繞方式是順時鐘的(CW),表面法向量則是指向多邊形的,這樣你看到的是多邊形的背面而不是前面。你可以使用gluTessNormal()來明確地指明表面法向量。

    [cpp]?view plaincopy
  • void?gluTessNormal(GLUtessellator?*tess,?GLdouble?x,?GLdouble?y,?GLdouble?z)??
  • 如果你用(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
  • 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網(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
  • void?combineCB(GLdouble?newVert[3],?GLdouble?*neighbourVert[4],??
  • ???????????????GLfloat?neighborWeight[4],?void?**outData);??

  • 當(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
  • 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();??

  • 在這個多邊形中考慮環(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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。