歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

OpenGL超級寶典學習筆記——頂點數組

頂點數組

當我們有來自模型的大量數據的時候,使用顯示列表來對這些數據進行預編譯,需要遍歷這些頂點數據(一次一個頂點數據)把數據傳給OpenGL。依賴於頂點的數量,這會帶來潛在的性能損耗。而且這些數據不一定是靜態的,有可能在我們每次渲染的時候,我們需要對這些數據進行更改。這個時候就不適合使用顯示列表。

在OpenGl中,使用頂點數組能夠很好的解決這兩個問題。使用頂點數組,我們可以隨時進行預編譯或修改幾何圖形,然後一次性傳輸這些數據。基本的頂點數組幾乎和顯示列表一樣快,而且不要求數據是靜態的。

在OpenGL中使用頂點數組有4個基本的步驟:

  1. 把幾何圖形的數據加載到一個或多個數組中(可以從磁盤中讀取)
  2. 告訴OpenGL數據在哪裡(參數傳指向數據的指針)
  3. 你需要使用哪些數組,因為數組可能分為頂點數組,顏色數組,紋理坐標數組等,或者一個數組中包含這些數據,按照某種順序排列。
  4. 執行OpenGL命令使用你的頂點數據進行渲染。

為了演示這些步驟,修改之前的第九章的pointsprite的例子。我們使用頂點數組的方式替代掉glBegin/glEnd的方式。修改的代碼如下:

  //畫小星星 glPointSize(7.0);

  glVertexPointer(2, GL_FLOAT, 0, &smallStars[0]);
  glDrawArrays(GL_POINTS, 0, SMALL_NUM);  ///畫中等大小的星星  glPointSize(12.0);

  glVertexPointer(2, GL_FLOAT, 0, &mediumStars[0]);
  glDrawArrays(GL_POINTS, 0, MEDIUM_NUM);  ////大星星  glPointSize(20.0);

  glVertexPointer(2, GL_FLOAT, 0, &largeStars[0]);
  glDrawArrays(GL_POINTS, 0, LARGE_NUM);
  glDisableClientState(GL_VERTEX_ARRAY);

 

 

加載幾何圖形

首先我們需要把幾何圖形的數據組裝到數組中。在上面的例子中,在初始化的時候就組裝好數據,代碼如下:

//星星的坐標 M3DVector2f smallStars[SMALL_NUM];
M3DVector2f mediumStars[MEDIUM_NUM];
M3DVector2f largeStars[LARGE_NUM];
void SetupRC()
{
... //隨機獲取星星的位置  for (int i = 0; i < SMALL_NUM; ++i)
  {
    smallStars[i][0] = (GLfloat)(rand() % SCREEN_X);
    smallStars[i][1] = (GLfloat)(rand() % SCREEN_Y);
  }  for (int i = 0; i < MEDIUM_NUM; ++i)
  {
    mediumStars[i][0] = (GLfloat)(rand() % SCREEN_X);
    mediumStars[i][1] = (GLfloat)((rand() % SCREEN_Y) + 50);
  }

 for (int i = 0; i < LARGE_NUM; ++i)
  {
    largeStars[i][0] = (GLfloat)(rand() % SCREEN_X);
    largeStars[i][1] = (GLfloat)(rand() % SCREEN_Y);
  }
...
}

 

啟用數組

像OpenGL大多數的特性一樣,要使用頂點數組首先得啟用它。

//使用頂點數組

glEnableClientState(GL_VERTEX_ARRAY);

啟用和禁用的函數原型如下:

void glEnableClientState(GLenum array);

void glDisableClientState(GLenum array);

函數接受的參數值有:GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_SECONDARY_COLOR_ARRAY, GL_NORMAL_ARRAY, GL_FOG_CORRDINATE_ARRAY, GL_TEXTURE_COORD_ARRAY和GL_EDGE_FLAG_ARRAY。在上面的例子中我們只使用到了頂點數組。當然我們可以同時啟用多種類型的數組。

為什麼是使用glEnableClientState來啟用數組而不是像以前那樣用glEnable?因為OpenGL的設計時 client/server模式的。server服務器是圖形硬件,client客戶端是CPU和內存。對於PC來說,服務器就是顯卡,客戶端就是CPU和主存。

 

指定數據

在我們啟用了頂點數組之後,我們需要告訴OpenGL數據在哪裡(內存中的位置)。在上面的例子中相應的代碼:

glVertexPointer(2, GL_FLOAT, 0, &smallStars[0]);

相應的有指定顏色數組,紋理坐標數組等等,列表如下:

//頂點 void glVertexPointer(GLint size, GLenum type, GLsizei stride, const void *pointer); //顏色 void glColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer); //紋理坐標 void glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer); //輔助顏色 void glSecondaryColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer); //法線 void glNormalPointer(GLenum type, GLsizei stride, const void *pData); //霧坐標 void glFogCoordPointer(GLenum type, GLsizei stride, const void *pointer); //邊界 void glEdgeFlagPointer(GLenum type, GLsizei stride, const void *pointer);

上面同類型的參數意義都是一樣的。 其中第一個參數size是指一個頂點或顏色等所包含的元素的個數,例如頂點有(x,y), (x,y,z), (x,y,z,w)的形式。像法線,霧坐標, 邊界標記這幾個函數沒有size,因為它們的值一定是3(x,yz)的形式。

參數type指的是數據的類型,並不是所有的數據類型都可以被接受的。什麼類型的數組能接受的數據類型和元素個數如下:

stride參數指定了數據之間的間隔。例子中的情況是0,為我們的頂點數據是緊挨著的。如果我們一個數組中即包含了頂點數據和顏色數據(混合數組),那麼我們可以通過這個stride來區分。舉個例子:

  GLfloat data[] =
  {
    10.0f, 5.0f, 0.0f, //頂點數據  1.0f, 0.0f, 0.0f , //顏色數據  5.0f, 10.0f, 0.0f, //頂點數據  0.0f, 1.0f, 0.0f //顏色數據 }

  glVertexPointer(3, GL_FLOAT, 3, &data[0]); //此時頂點數據的間隔就是3  glColorPointer(3, GL_FLOAT, 3, &data[3]); //此時顏色數據的間隔也是3

對於多重紋理的情況,如果我們是使用glBegin/glEnd的方式,那可以通過glMultiTexCoord來指示為哪一個紋理指定坐標。如果使用頂點數組的方式,那麼我們可以在調用glTexCoordPointer之前調用:

glClientActiveTexture(GLenum texture);

其中texture是GL_TEXTURE0, GL_TEXTURE1等。來指定是哪一個紋理的坐標。

 

用數據繪制

到此為止OpenGl已經知道我們數據的位置了,那麼我們可以用下面的代碼遍歷我們的數據:

glBegin(GL_POINTS); for(i = 0; i < SMALL_STARS; i++)
    glArrayElement(i);
glEnd();

glArrayElement會從數組中提取相應的數據。假設我們已經啟用和設置好了頂點,顏色,紋理坐標數組。那麼上面的函數調用相當於:

  glBegin(GL_POINTS); for (i = 0; i < SMALL_STARS; ++i)
    {
      glColor3fv(color[i]);
      glTexCoord3fv(texcoord[i]);
      glVertex3fv(vertex[i]);
    }
  glEnd();

當然OpenGL提供了一種更簡便快速的方法:

void glDrawArrays(GLenum mode, GLint first, GLint count);

其中mode指定了渲染的圖元模式GL_POINTS, GL_TRIANGLES等等。第二個參數first指定了頂點數組起始的下標,count指定了要使用的頂點的個數。在上面的例子中,渲染小星星的方式如下:

glDrawArrays(GL_POINTS, 0, SMALL_NUM);

這樣OpenGL的實現可以優化這些數據塊傳輸的過程,也節省了許多函數的調用。

 

頂點索引數組

頂點索引數組存儲的是頂點數組的索引(數組的下標)。這樣一來改變頂點遍歷的順序,其訪問順序是由一個單獨的索引數組指定的。二來頂點數組可以減少存儲頂點的數量,一些幾何圖形有許多的共享頂點,如果使用頂點索引數組的方式,這些共享的頂點就沒必要重復存儲在頂點數組中(許多情況下可以節省內存空間,節省傳輸的帶寬,也減少對內存的操作),也減少了變換的開銷。在理想的情況下,他們可能比顯示列表更快。

雖然三角形帶(GL_TRIANGLE_STRIPS)能夠共享頂點。但沒辦法避免兩個三角形帶所共享頂點的變換的開銷,因為每一個三角形帶都必須是獨立的。

下面舉一個簡單的例子。

 

簡單的立方體

一個立方體有6個面,每個面都是由4個頂點組成的正方形,6x4=24個頂點,其實有許多被正方形共享的頂點,不重復的頂點只有8個。但按照以往的方式使用glBegin(GL_UQADS)/glEnd,我們還是需要傳輸24個頂點(調用glVertex 24次)。如果我們使用頂點索引數組的方式,就只需要8個頂點就夠了,我們用索引指向這些頂點,索引數組中會有重復的值。圖示如下:

每個頂點有浮點數值組成的,但每個索引只是一個整數值。在頂點數少的情況下,並不會節省多少空間。比如這個立方體,雖然頂點數組少存了16個頂點,但是索引數組需要額外的24個整數值來存儲這些頂點的索引的。

代碼示例:

static GLfloat cube[]={-1.0f, -1.0f, -5.0f, //前面的正方形  1.0f, -1.0f,-5.0f,
1.0f, 1.0f, -5.0f, 
-1.0f, 1.0f, -5.0f,
-1.0f, -1.0f, -10.0f,//背面的正方形  1.0f, -1.0f, -10.0f,
1.0f, 1.0f, -10.0f,
-1.0f, 1.0f, -10.0f}; static GLubyte index[]={0, 1, 2, 3, //前面  0, 3, 7, 4, //左面  5, 6, 2, 1, //右面  7, 6, 5, 4, //後面  3, 2, 6, 7, //上面  1, 0, 4, 5 //地面 }; void SetupRC()
{
  glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
} void RenderScene()
{
  glClear(GL_COLOR_BUFFER_BIT);

  glColor3f(0.0f, 0.0f, 1.0f);

  glPushMatrix();
  glEnableClientState(GL_VERTEX_ARRAY);
  glVertexPointer(3, GL_FLOAT, 0, cube);

  glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, index);

  glDisableClientState(GL_VERTEX_ARRAY);
  glPopMatrix();
  glutSwapBuffers();
}

可以看到上面調用繪制的��數是

glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, indexes);

第一個參數是圖元的模式,第二個是索引數組包含的值的個數,第三個參數索引數組值的類型,最後一個參數是索引數組的指針。還有其他相應的函數。

glDrawRangeElements 可以指定索引數組的起始和結束位置.

glInterleavedArrays可以使用混合數組。相關的函數請參考文檔。

OpenGL超級寶典 第4版 中文版PDF+英文版+源代碼 見  http://www.linuxidc.com/Linux/2013-10/91413.htm

OpenGL編程指南(原書第7版)中文掃描版PDF 下載 http://www.linuxidc.com/Linux/2012-08/67925.htm

OpenGL 渲染篇 http://www.linuxidc.com/Linux/2011-10/45756.htm

Ubuntu 13.04 安裝 OpenGL http://www.linuxidc.com/Linux/2013-05/84815.htm

OpenGL三維球體數據生成與繪制【附源碼】 http://www.linuxidc.com/Linux/2013-04/83235.htm

Ubuntu下OpenGL編程基礎解析 http://www.linuxidc.com/Linux/2013-03/81675.htm

如何在Ubuntu使用eclipse for c++配置OpenGL http://www.linuxidc.com/Linux/2012-11/74191.htm 

更多《OpenGL超級寶典學習筆記》相關知識 見 http://www.linuxidc.com/search.aspx?where=nkey&keyword=34581

Copyright © Linux教程網 All Rights Reserved