OpenGL幾種繪制方式
本文介紹了OpenGL的幾種繪制方式及各自特點。繪制方式如下:
立即模式
顯示列表
頂點數組
VBO
1、立即模式
最直接的方式,傳統的使用glBegin...glEnd繪制的方式,如下所示:
glBegin( GL_TRIANGLES );
glVertex3f(-1.0f, -0.5f, -4.0f);
glVertex3f( 1.0f, -0.5f, -4.0f);
glVertex3f( 0.0f, 0.5f, -4.0f);
glEnd();
這種方式效率較低。原因是
glVertex函數每次調用只把一個頂點從客戶端(CPU或內存)傳輸到服務端(GPU),而這個傳輸的過程相對於GPU處理數據的過程是很慢的。
glVertex函數的調用次數過多
2、顯示列表
將圖形繪制進行預編譯,把繪制好的圖形放到GPU,使用的時候直接調用。
初始化函數
displayList = glGenLists( 1 ); //請求顯示列表名稱
glNewList( displayList, GL_COMPILE ); //創建顯示列表
glBegin( GL_TRIANGLES );
glVertex3f(-1.0f, -0.5f, -4.0f);
glVertex3f( 1.0f, -0.5f, -4.0f);
glVertex3f( 0.0f, 0.5f, -4.0f);
glEnd();
glEndList();
渲染函數
glCallList( displayList );
顯示列表的優化策略是將圖形繪制命令集(數據塊)存儲在GPU中,無須CPU到GPU間的數據傳遞,節省了時間。
然而顯示列表雖然提升了繪制速度,但是它一旦創建了就是不可修改的,如果要修改,只能銷毀並重新創建顯示列表,所以它適用於那些靜態的圖形。
3、頂點數組
將數據保存在數組中,當執行繪制(glDrawArrays或glDrawElements)的時候一次性將數據從CPU傳遞到GPU中。
//初始化頂點數組
GLfloat vertices[ 3 ][3] = {0};
vertices[0][0] = -1.0f;
vertices[0][1] = -0.5f;
vertices[0][2] = -4.0f;
vertices[1][0] = 1.0f;
vertices[1][1] = -0.5f;
vertices[1][2] = -4.0f;
vertices[2][0] = 0.0f;
vertices[2][1] = 0.5f;
vertices[2][2] = -4.0f;
glEnableClientState( GL_VERTEX_ARRAY ); //啟用頂點數組
glVertexPointer( 3, GL_FLOAT, 0, vertices ); //指定數據
glDrawArrays( GL_TRIANGLES, 0, 3); //進行繪圖
glDisableClientState( GL_VERTEX_ARRAY );
頂點數據的優化策略是減少數據從CPU到GPU的傳遞次數,從而節省了時間。但是它仍然有數據的傳遞,所以繪制效率不如顯示列表高。相比於顯示列表它的優點是數據可以動態修改。
4、VBO
VBO將頂點數據存儲在GPU緩存中,無須CPU到GPU的數據傳遞,並且可以動態修改。
初始化函數
//初始化頂點數組
GLfloat vertices[ 3 ][3] = {0};
vertices[0][0] = -1.0f;
vertices[0][1] = -0.5f;
vertices[0][2] = -4.0f;
vertices[1][0] = 1.0f;
vertices[1][1] = -0.5f;
vertices[1][2] = -4.0f;
vertices[2][0] = 0.0f;
vertices[2][1] = 0.5f;
vertices[2][2] = -4.0f;
glewInit();
glGenBuffers( 1, buffer ); //創建緩沖區
//指定緩沖區數據
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 3, vertices, GL_DYNAMIC_DRAW );
渲染函數
glBindBuffer( GL_ARRAY_BUFFER, buffer ); //綁定緩沖區
glEnableClientState( GL_VERTEX_ARRAY ); //使用VBO必須開啟頂點數組
glVertexPointer( 3, GL_FLOAT, 0, 0 );
glDrawArrays( GL_TRIANGLES, 0, 3 ); //繪制
glDisableClientState( GL_VERTEX_ARRAY );
glBindBuffer( GL_ARRAY_BUFFER, 0 ); //取消綁定緩沖區
關於VBO的動態修改,可以將數據映射到客戶端(內存)中,然后再進行修改。以下是一種刷新所有頂點數據的方法。
//初始化新數組
GLfloat newvertices[3][3] = {0};
newvertices[0][0] = 0.0f;
newvertices[0][1] = 0.0f;
newvertices[0][2] = 50.0f;
...
//映射緩沖區
glBindBuffer( GL_ARRAY_BUFFER, buffer] );
glBufferData( GL_ARRAY_BUFFER, sizeof( GLfloat ) * 3* 3, NULL, GL_STREAM_DRAW );
GLvoid* PositionBuffer = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY );
//刷新數據
memcpy( PositionBuffer, newvertices, sizeof( GLfloat ) * 1002 * 3 );
//刷新到VBO
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glUnmapBuffer( GL_ARRAY_BUFFER );
glVertexPointer( 3, GL_FLOAT, 0, 0 );
VBO結合了顯示列表與頂點數組的優點,既在GPU保存數據避免數據傳輸,提高了繪制效率,又可以動態修改。
[參考]
OpenGL超級寶典(第四版)