OpenGL頂點數組

概述

做爲在當即模式(glBegin()與glEnd()之間)下指定單個頂點數據的替代,你能夠保存頂點數據在一組列表中,包括頂點位置、法線、紋理座標與顏色信息。而且你能夠經過索引數組解引用數組元素繪製選定的幾何圖元。html

看看下面的用當即模式繪製立方體的代碼。數組

glBegin(GL_TRIANGLES);  // draw a cube with 12 triangles
    // 前面 =================
    glVertex3fv(v0);    // v0-v1-v2
    glVertex3fv(v1);
    glVertex3fv(v2);

    glVertex3fv(v2);    // v2-v3-v0
    glVertex3fv(v3);
    glVertex3fv(v0);

    // 右面 =================
    glVertex3fv(v0);    // v0-v3-v4
    glVertex3fv(v3);
    glVertex3fv(v4);

    glVertex3fv(v4);    // v4-v5-v0
    glVertex3fv(v5);
    glVertex3fv(v0);

    // 上面 ===================
    glVertex3fv(v0);    // v0-v5-v6
    glVertex3fv(v5);
    glVertex3fv(v6);

    glVertex3fv(v6);    // v6-v1-v0
    glVertex3fv(v1);
    glVertex3fv(v0);

    ...                 // 繪製其他3面

glEnd();
image

爲構造每一個面的2個三角形,須要調用glVertex*()6次。例如,正面分爲v0-v1-v2與v2-v3-v0兩個三角形。一個立方體有6個面,所以glVertex*()的調用次數爲36。若是你還需爲相關頂點指定法線、紋理座標與顏色,這增長對OpenGL函數的調用。緩存

另外一個須要注意的是:頂點「v0」被三個相鄰的面共用:正面、右面與頂面。在當即模式下,你必須提供這個共用點6次,就像代碼中那樣每一個面2次。ide

使用頂點數字會下降函數調用次數及共用頂點的重複使用。所以,能夠提供渲染效率。在此,解釋3種不一樣的使用頂點數組的OpenGL函數:glDrawArrays()、glDrawElements()與glDrawRangeElements()。然而,更好的方法是使用頂點緩存對象(VBO)與顯示列表。函數

初始化

OpenGL提供glEnableClientState()與glDisableClientState()函數啓用/禁用6中不一樣類別的數組。此外,有6個函數用於指定數組的精確位置(地址),所以在你的應用程序中OpenGL能夠訪問這些數組。性能

  • glVertexPointer():指定頂點座標數組指針
  • glNormalPointer():指定法線數組指針
  • glColorPointer():指定RGB顏色數組指針
  • glIndexPointer():指定索引顏色數組指針
  • glTexCoordPointer():指定紋理座標數組指針
  • glEdgeFlagPointer():指定邊標誌數組指針

每一個函數都須要不一樣的參數。能夠參考OpenGL API手冊。邊標誌用於標記頂點是否在邊界上。所以,若是glPolygonMode()爲GL_LINE時,只有邊具備邊標記的那些邊是可見的。ui

glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer)

  1. size:頂點座標數量,對於2D點爲2,3D點爲3。
  2. type:GL_FLOAT、GL_SHORT、GL_INT或GL_DOUBLE。
  3. stride:連續兩個頂點間的字節偏移量(用於交叉數組)。
  4. pointer:頂點數組指針。

glNormalPointer(GLenum type, GLsizei stride, const GLvoid* pointer)

  1. type:GL_FLOAT、GL_SHORT、GL_INT或GL_DOUBLE。
  2. stride:連續兩個法線間的字節偏移量(用於交叉數組)。
  3. pointer:法線數組指針。

注意,頂點數組保存在你的應用程序(系統內存),它在客戶端。且處在服務端的OpenGL訪問它們。這就是爲何擁有頂點數組這些特殊命令的緣由,使用glEnableClientState()與glDisableClientState()而不是glEnable()與glDisable()。指針

glDrawArrays()

glDrawArrays()從開啓的數組中順序讀取頂點數據。因爲glDrawArray()不許許在頂點數組中跳躍,你必須爲每一個面重復指定共用頂點。code

glDrawArrays()具備3個參數。第一個參數爲圖元類型。第二個參數爲數組的其實偏移位置。最後一個參數爲傳遞給OpenGL渲染管線的頂點數量。在上面繪製立方體的實例中,第一個參數爲GL_TRIANGLES,第二個參數爲0,即從數組開始讀取。最後一個參數爲36:立方體具備6個面,且每一個面須要繪製兩個三角形的6個頂點,6×6=36。orm

GLfloat vertices[] = {...}; // 36頂點座標
...
// 啓用並指定頂點數組指針
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);

// 繪製立方體
glDrawArrays(GL_TRIANGLES, 0, 36);

// 在繪製以後禁用頂點數組
glDisableClientState(GL_VERTEX_ARRAY);

因爲使用glDrawArrays(),你能夠用單個glDrawArrays()調用替換36次的glVertex*()調用。然而,咱們依舊須要重複指定共用頂點,所以數組中的頂點數量依舊爲36個,而不是8個。glDrawElements()是下降數組中頂點數量的方法,所以,它准許向OpenGL傳遞更少的數據。

glDrawElements()

glDrawElements()經過頂點數組相關的隨機數組索引繪製圖元序列。它下降函數調用次數與頂點傳遞數量。此外,OpenGL能夠緩存最近處理過的頂點以及重用它們,而沒必要向頂點變換管線重複發送相同的頂點。

glDrawElements()須要4個參數。第一個參數爲圖元類型,第二個爲索引數組數量,第三個位索引數組的數據類型,最後一個參數爲索引數組地址。在此例中,參數分別爲:GL_TRIANGLES、3六、GL_UNSIGNED_BYTE與indices。

GLfloat vertices[] = {...};          // 8個頂點座標
GLubyte indices[] = {0,1,2, 2,3,0,   // 36個索引
                     0,3,4, 4,5,0,
                     0,5,6, 6,1,0,
                     1,6,7, 7,2,1,
                     7,4,3, 3,2,7,
                     4,7,6, 6,5,4};
...
// 啓用並指定頂點數組指針
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);

// 繪製立方體
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);

// 繪製以後禁用頂點數組
glDisableClientState(GL_VERTEX_ARRAY);

如今,頂點數組的大小爲8,這就是立方體的頂點數量,而沒有任何重複。

注意,索引數組的數據類型爲GLubyte,而不是GLuint或GLushort。爲了下降索引數組的大小,它應該是可以容納索引數量的最小數據類型,不然,因爲索引數組過大可以引發性能降低。由於頂點數組包含8個頂點,GLubyte足夠容納全部索引值。

另外一個須要考慮的問題是共用頂點上的法向量。若是共用頂點的相鄰多邊形的法線各不相同,則法向量須要同面的數量同樣多,每一個面一個法向量。

例如,頂點v0爲正面、右面與頂面所共用,不過法線並不能共用。正面的法線爲n0,右面的法線爲n1,頂面的法線爲n2。對於這種狀況,法線並不與共用頂點相同,頂點就不可以僅在頂點數組中指定一次。爲了匹配法向數組中元素的數量,頂點數組中必須屢次指定該頂點座標。具備法線的典型立方體須要24個單一頂點:6個面×每面4個頂點。參考示例代碼中的實際實現。

glDrawRangeElements()

glDrawElements()相似,glDrawRangeElements()也適用於隨機訪問頂點數組。不過glDrawRangeElements()有額外的2個參數(start與end索引)以指定須要讀取的頂點範圍。經過增長該範圍限定,OpenGL可以在渲染以前僅獲取限定數量的頂點數組,且可以提升性能。

glDrawRangeElements()中新增的參數爲start與end索引,OpenGL從這些值(end-start+1)中獲取限定數量的頂點。而且索引數組中的值必須位於start與end索引之間。注意,並非範圍(start,end)之間的全部頂點都會被接引用。然而,若是你指定一個稀疏使用範圍,會引發沒必要要的對數組中未使用的那些頂點的處理。

GLfloat vertices[] = {...};          // 8個頂點座標
GLubyte indices[] = {0,1,2, 2,3,0,   // 第一部分(18個索引)
                     0,3,4, 4,5,0,
                     0,5,6, 6,1,0,

                     1,6,7, 7,2,1,   // 第二部分(18個索引)
                     7,4,3, 3,2,7,
                     4,7,6, 6,5,4};
...
// 開啓並指定頂點數組指針
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);

// 繪製第一部分, 範圍爲6 - 0 + 1 = 7用到的頂點
glDrawRangeElements(GL_TRIANGLES, 0, 6, 18, GL_UNSIGNED_BYTE, indices);

// 繪製第二部分, 範圍爲6 - 0 + 1 = 7用到的頂點
glDrawRangeElements(GL_TRIANGLES, 1, 7, 18, GL_UNSIGNED_BYTE, indices+18);

// 繪製以後禁用頂點數組
glDisableClientState(GL_VERTEX_ARRAY);

你能夠經過使用GL_MAX_ELEMENTS_VERTICES與GL_MAX_ELEMENTS_INDICES調用glGetIntegerv()查詢可獲取頂點的最大數量與可引用索引的最大數量。

注意,glDrawRangeElements()在OpenGL 1.2或更高版本有效。

實例

該實例程序以4中不一樣方式繪製立方體:當即模式、glDrawArrays()、glDrawElements()與glDrawRangeElements()。

  • draw1():以當即模式繪製立方體。
  • draw2():以glDrawArrays()繪製立方體。
  • draw3():以glDrawElements()繪製立方體。
  • draw4():以glDrawRangeElements()繪製立方體。
  • draw5():以glDrawElements()與間隔頂點數組方式繪製立方體。

下載源代碼與二進制文件:vertexArray.zip

爲了正確執行該程序,顯卡必須支持OpenGL v1.2或更高。經過glinfo確認你的顯卡驅動是否支持OpenGL v1.2或更高。

 

英文原文:http://www.songho.ca/opengl/gl_vertexarray.html

相關文章
相關標籤/搜索