做爲在當即模式(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();
爲構造每一個面的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能夠訪問這些數組。性能
每一個函數都須要不一樣的參數。能夠參考OpenGL API手冊。邊標誌用於標記頂點是否在邊界上。所以,若是glPolygonMode()爲GL_LINE時,只有邊具備邊標記的那些邊是可見的。ui
注意,頂點數組保存在你的應用程序(系統內存),它在客戶端。且處在服務端的OpenGL訪問它們。這就是爲何擁有頂點數組這些特殊命令的緣由,使用glEnableClientState()與glDisableClientState()而不是glEnable()與glDisable()。指針
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()經過頂點數組相關的隨機數組索引繪製圖元序列。它下降函數調用次數與頂點傳遞數量。此外,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個頂點。參考示例代碼中的實際實現。
與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()。
下載源代碼與二進制文件:vertexArray.zip
爲了正確執行該程序,顯卡必須支持OpenGL v1.2或更高。經過glinfo確認你的顯卡驅動是否支持OpenGL v1.2或更高。