VAO和VBO API備忘

  • 概述

OpenGL從OpenGL 3.0開始將API分紅了兩種類型:即舊式的OpenGL(Legacy OpenGL)和新式的OpenGL(Core Profile),OpenGL3.3 的官方API文檔完整地描述了新式的OpenGL API,舊式的OpenGL API能夠在OpenGL 2.1中查看。html

對於OpenGL渲染的的第一站:也就是把頂點數據(包括頂點位置、頂點法線、頂點顏色、紋理座標等)傳入到OpenGL,舊式的OpenGL中有如下幾種方式實現:數組

  1. 當即模式(glBegin/glEnd) 這種方式恐怕是學習OpenGL最先接觸到的API吧,至少我是這樣。不過很遺憾它已經被新式OpenGL拋棄了
  2. 頂點數組(VA:Vertex Array)相比當即模式減小了函數的調用開銷。目前也被新式OpenGL拋棄
  3. 緩衝區對象(VBO:Vertex Buffer Object)相比較VA,將數據存儲到服務器端(VA存儲在客戶端)。目前是新式OpenGL支持的惟一數據傳入方式
  4. 顯示列表(Display List)將若干條OpenGL命令編譯好,直接由顯卡調用。因爲編譯好的模塊沒法修改,喪失了靈活性。也被新式OpenGL拋棄

此外VBO在Legacy OpenGL中和Core Profile OpenGL中的使用也有着不一樣的方式。服務器

  • VA API

(1) 開啓和關閉VAide

//開啓和關閉客戶端狀態  

void glEnableClientState(GLenum cap);  

void glDisableClientState(GLenum cap);  

  

//參數cap取值  

GL_VERTEX_ARRAY                 //頂點位置        

GL_COLOR_ARRAY                  //頂點顏色  

GL_EDGE_FLAG_ARRAY              //頂點邊界線標識  

GL_FOG_COORD_ARRAY              //頂點霧座標  

GL_INDEX_ARRAY                  //頂點索引  

GL_NORMAL_ARRAY                 //頂點法線  

GL_SECONDARY_COLOR_ARRAY            //頂點輔助顏色  

GL_TEXTURE_COORD_ARRAY              //頂點紋理座標  

(2) 設置數據到頂點函數

//////////////////////////////////////////////////////////////////////////  
//如下是一系列設置頂點數據的API  
//參數取值(默認形參表明OpenGL中默認值):  
// size   描述數據的維度(2D\3D)   
// type   描述每一個數據的類型   
// stride 描述每一個頂點數據的跨度  
//pointer 指向實際數據  
  
  
//設置頂點位置數據  
void glVertexPointer( GLint size=4,  GLenum type=GL_FLOAT,  GLsizei stride=0,  const GLvoid *pointer=0);   
//設置頂點顏色數據  
void glColorPointer( GLint size=4,  GLenum type=GL_FLOAT,  GLsizei stride=0,  const GLvoid *pointer=0);   
//設置頂點邊界線標識數據  
void glEdgeFlagPointer( GLsizei stride=0,  const GLvoid *pointer=0);   
//設置頂點霧座標數據  
void glFogCoordPointer( GLenum type=GL_FLOAT, GLsizei stride=0,  GLvoid *pointer=0);   
//設置頂點索引數據  
void glIndexPointer( GLenum type=GL_FLOAT,  GLsizei stride=0,  const GLvoid *pointer=0);   
//設置頂點法線數據  
void glNormalPointer( GLenum type=GL_FLOAT,  GLsizei stride=0,  const GLvoid *pointer=0);   
//設置頂點輔助顏色數據  
void glSecondaryColorPointer( GLint size=3,  GLenum type=GL_FLOAT,  GLsizei stride=0,  const GLvoid *pointer=0);   
//設置頂點紋理座標數據  
void glTexCoordPointer( GLint size=4,  GLenum type=GL_FLOAT,  GLsizei stride=0,  const GLvoid *pointer=0);   

(3) VA繪製學習

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
//經過設置好的頂點數據進行繪製:可使用下列API進行繪製  
  
void glDrawArrays( GLenum mode,  GLint first,  GLsizei count);  
//參數描述  
//mode    繪製幾何圖元類型(GL_POINTS, GL_LINE_STRIP, GL_TRIANGLE等)  
//first      頂點數據的開始索引位置(對應(2)中pointer)  
//count    須要渲染的頂點數據數量  
  
void glDrawElements( GLenum mode,  GLsizei count,  GLenum type,  const GLvoid *indices);   
//參數描述  
//mode     繪製幾何圖元類型(GL_POINTS, GL_LINE_STRIP, GL_TRIANGLE等)  
//count     第四個參數indices中參與渲染的索引數量  
//type       第四個參數indices的數據類型  
//indices  須要額外傳入的一個索引數組指針  
  
void glDrawRangeElements( GLenum mode, GLuint start,  GLuint end,  GLsizei count,  GLenum type,  const GLvoid * indices);  
//參數描述(與glDrawElements一致但多出兩個參數)  
//mode     繪製幾何圖元類型(GL_POINTS, GL_LINE_STRIP, GL_TRIANGLE等)  
//count     第四個參數indices中參與渲染的索引數量  
//type       第四個參數indices的數據類型  
//indices  須要額外傳入的一個索引數組指針  
//start       索引數組indices中的最小值索引值  
//end        索引數組indices中的最大值索引值  

以上就是VA所涉及到的基本API,使用過程以下優化

首先開啓客戶端VertexArray狀態,接着綁定數據到頂點狀態,最後進行繪製:ui

static float vertices[][3] = {  
    1.0f, 0.0f, 0.0f,  
    0.0f, 1.0f, 0.0f,  
    -1.0f, 0.0f, 0.0f  
};  
  
static float colors[][4] = {  
    1.0f, 0.0f, 0.0f, 1.0f,  
    0.0f, 1.0f, 0.0f, 1.0f,  
    0.0f, 0.0f, 1.0f, 1.0f  
};  
  
//繪製函數  
void renderScene()  
{  
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  
    glMatrixMode(GL_MODELVIEW);  
    glLoadIdentity();  
    glTranslatef(0.0f, 0.0f, -5.0f);  
      
    //////////////////////////////////////////////////////////////////////////  
    //開啓VA狀態  
    glEnableClientState(GL_VERTEX_ARRAY);  
    glEnableClientState(GL_COLOR_ARRAY);  
    //綁定數據  
    glVertexPointer(3, GL_FLOAT, 0, vertices);  
    glColorPointer(4, GL_FLOAT, 0, colors);  
    //繪製  
    glDrawArrays(GL_TRIANGLES, 0, 3);  
    //關閉VA狀態  
    glDisableClientState(GL_VERTEX_ARRAY);  
    glDisableClientState(GL_COLOR_ARRAY);  
    //////////////////////////////////////////////////////////////////////////  
}     
  • VBO API

前文描述了VBO在Legacy 和Core Profile中有兩種不一樣的方式:spa

  • Legacy OpenGL VBO API

(1)建立(初始化)VBO對象指針

//建立VBO的API  
//////////////////////////////////////////////////////////////////////////  
void glGenBuffers( GLsizei   n, GLuint *   buffers);   
//生成緩衝區ID  
//參數描述  
// n            生成ID數量  
// buffers 存儲緩衝區ID的數組  
  
void glBindBuffer( GLenum   target,  GLuint   buffer);   
//設置緩衝區爲當前操做的緩衝區,而且設置緩衝區的類型  
//參數描述  
//target            緩衝區類型(包括4種)  
GL_ARRAY_BUFFER  
GL_ELEMENT_ARRAY_BUFFER  
GL_PIXEL_PACK_BUFFER  
GL_PIXEL_UNPACK_BUFFER  
//buffer        使用glGenBuffers生成的緩衝區ID  
  
void glBufferData( GLenum   target,  GLsizeiptr   size,  const GLvoid *   data,  GLenum   usage);   
//爲緩衝區設置數據  
//參數描述  
//target            緩衝區類型(包括4種)  
GL_ARRAY_BUFFER  
GL_ELEMENT_ARRAY_BUFFER  
GL_PIXEL_PACK_BUFFER  
GL_PIXEL_UNPACK_BUFFER  
//size             緩衝區大小(多少字節)  
//data         實際指向數組的指針  
//usage        當前緩衝區的使用模式[一種提示用來優化之用](取值以下9種)  
GL_STREAM_DRAW  
GL_STREAM_READ  
GL_STREAM_COPY  
GL_STATIC_DRAW  
GL_STATIC_READ  
GL_STATIC_COPY  
GL_DYNAMIC_DRAW  
GL_DYNAMIC_READ  
GL_DYNAMIC_COPY  

(2)開啓/關閉VBO

當建立完VBO以後,咱們並不知道VBO中存儲的是頂點位置、仍是頂點顏色或者是頂點法線,因而使用下面的API來描述某一個VBO中究竟是頂點什麼方面的數據

//////////////////////////////////////////////////////////////////////////  
//開啓關閉VBO狀態  
void glEnableClientState(GLenum cap);  
void glDisableClientState(GLenum cap);  
//參考VA中的描述  
  
  
void glVertexPointer( GLint   size,  GLenum   type,  GLsizei   stride,  const GLvoid *   pointer);   
//參考VA中的描述:  
//可是pointer參數有所區別:  
//當使用了glBindBuffer將GL_ARRAY_BUFFER綁定到一個非0的VBO ID上以後,這時候的pointer  
//表明的是一個VBO ID對象中實際數據(glBufferData中data參數)的偏移值,也就是說這時候pointer  
//並非一個指針,僅僅是一個偏移值。同時GL_ARRAY_BUFFER_BINDING這個狀態會保存在客戶端  
  
//綜合上面VA中的解釋:能夠知道這個glVertexPointer中的pointer兩種含義:  
//(1)當glBindBuffer綁定到非0的ID時:  
//pointer表明偏移,此時客戶端glClientState中會保存GL_ARRAY_BUFFER_BINDING暗示如今在用VBO  
//(2)當glBindBuffer綁定到0的ID時:  
//pointer表明一個指向數組的指針,此時客戶端glClientState中不會保存GL_ARRAY_BUFFER_BINDING暗示如今在用VA  
  
//同理,對於VA中用到的一系列函數都有一樣的解釋,再也不贅述:  
glColorPointer  
glEdgeFlagPointer  
glFogCoordPointer  
glIndexPointer  
glNormalPointer  
glSecondaryColorPointer  
glTexCoordPointer 

 

(3)VBO繪製

//////////////////////////////////////////////////////////////////////////  
//繪製VBO  
  
void glDrawArrays( GLenum mode,  GLint first,  GLsizei count);  
//參考VA中描述  
//所不一樣的是數據如今在緩衝區之中,而不是VA所指向的pointer數組中了  
  
void glDrawElements( GLenum mode,  GLsizei count,  GLenum type,  const GLvoid *indices);  
//參考VA中描述  
//所不一樣的是indices設置爲NULL  
//由於緩衝區類型有一種是GL_ELEMENT_ARRAY_BUFFER(查看glGenBuffers中類型描述)  
//咱們會把indices索引值存儲在GL_ELEMENT_ARRAY_BUFFER類型的Buffer中,不須要額外的數組了  
//直接用兩個緩衝區便可 

 

以上即是舊式OpenGL中實現VBO的API,使用過程以下:

首先建立緩衝區並設置緩衝區類型和填滿緩衝區,接着指定緩衝裏面是什麼(到底存的是位置仍是顏色或者是法線),最後繪製

static float vertices[][3] = {  
    1.0f, 0.0f, 0.0f,  
    0.0f, 1.0f, 0.0f,  
    -1.0f, 0.0f, 0.0f  
};  
  
static float colors[][4] = {  
    1.0f, 0.0f, 0.0f, 1.0f,  
    0.0f, 1.0f, 0.0f, 1.0f,  
    0.0f, 0.0f, 1.0f, 1.0f  
};  
  
void renderScene()  
{  
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  
    glMatrixMode(GL_MODELVIEW);  
    glLoadIdentity();  
    glTranslatef(0.0f, 0.0f, -5.0f);  
      
    //////////////////////////////////////////////////////////////////////////  
    //建立VBO(通常放在初始化函數中)  
    GLuint vertexBufferID, colorBufferID;  
    glGenBuffers(1, &vertexBufferID);  
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);  
    glGenBuffers(1, &colorBufferID);  
    glBindBuffer(GL_ARRAY_BUFFER, colorBufferID);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);  
  
    //開啓VBO模式並設置VBO該怎麼解析  
    glEnableClientState(GL_VERTEX_ARRAY);  
    glEnableClientState(GL_COLOR_ARRAY);  
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);  
    glVertexPointer(3, GL_FLOAT, 0, 0);  
    glBindBuffer(GL_ARRAY_BUFFER, colorBufferID);  
    glColorPointer(4, GL_FLOAT, 0, 0);  
  
    //繪製  
    glDrawArrays(GL_TRIANGLES, 0, 3);  
  
    //繪製完成以後關閉VBO狀態  
    //將glBindBuffer設置爲0,使得後續glVertexPointer相似函數起到VA的做用  
    glDisableClientState(GL_VERTEX_ARRAY);  
    glDisableClientState(GL_COLOR_ARRAY);  
    glBindBuffer(GL_ARRAY_BUFFER, 0);  
    //////////////////////////////////////////////////////////////////////////  
}     
  • Core Profile VBO

(1)建立VBO

參考Legacy VBO中內容,兩者是同樣的

(2)開啓/關閉VBO

//////////////////////////////////////////////////////////////////////////  
void glEnableVertexAttribArray( GLuint   index);   
void glDisableVertexAttribArray( GLuint   index);   
//開啓和關閉一個通用的緩衝區對象  
//參數描述  
//index 緩衝區對象索引值  
  
void glVertexAttribPointer( GLuint   index,    
                                              GLint            size,    
                                              GLenum           type,    
                                              GLboolean        normalized,    
                                              GLsizei          stride,    
                                              const GLvoid *   pointer  
                                              );   
//設置如何解析緩衝區中的數據  
//參數描述  
//index             當前操做的緩衝區對象索引值  
//size              數據的維度是2D/3D/4D  
//type              數據類型 GL_INT /GL_FLOAT  
//normalized    是否已經單位化  
//stride                數據間距  
//pointer           參考Legacy VBO中解釋(在glBindBuffer未開啓時是指針開啓後是索引值) 

 

(3)繪製

參考Legacy VBO中內容,兩者是同樣的

以上即是Core Profile 下的VBO,使用方式以下:

首先建立VBO對象,接着開啓VBO而且用來講明VBO中數據是什麼樣組織的(可是並無說明數據是頂點位置、頂點顏色仍是法線),這一點與 Legacy VBO不一樣,由於它的說明部分(哪一個VBO存儲着位置、哪一個VBO存儲着顏色、哪一個VBO存儲着法線等)是在着色語言Shader中指明的,最後仍是使用 一樣的API繪製:

//////////////////////////////////////////////////////////////////////////  
GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,  
0.5f, 0.0f, 0.0f,  
0.0f, 0.5f, 0.0f };  
  
GLfloat vColors [] = { 1.0f, 0.0f, 0.0f, 1.0f,  
0.0f, 1.0f, 0.0f, 1.0f,  
0.0f, 0.0f, 1.0f, 1.0f };  
//////////////////////////////////////////////////////////////////////////  
  
void RenderWidget::paintGL()  
{  
    //////////////////////////////////////////////////////////////////////////  
    //生成VBO  
    glGenBuffers(1, &_vertexBuffer);  
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(vVerts), vVerts, GL_STATIC_DRAW);  
  
    glGenBuffers(1, &_colorBuffer);  
    glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(vColors), vColors, GL_STATIC_DRAW);  
  
    setShaders();  
  
    //開啓VBO並設置VBO裏面存儲的數據模式  
    glEnableVertexAttribArray(0);  
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);  
    glVertexAttribPointer(  
        0,  
        3,  
        GL_FLOAT,  
        GL_FALSE,   
        0,  
        (void*)0  
        );  
    glEnableVertexAttribArray(1);  
    glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);  
    glVertexAttribPointer(  
        1,  
        4,  
        GL_FLOAT,  
        GL_FALSE,  
        0,  
        (void*)0  
        );  
  
    //繪製VBO  
    glDrawArrays(GL_TRIANGLES, 0, 3);  
  
    //關閉VBO  
    glDisableVertexAttribArray(1);  
    glDisableVertexAttribArray(0);  
    glBindBuffer(GL_ARRAY_BUFFER, 0);  
}  

Shader部分

頂點Shader

#version 330  
layout(location = 0) in vec4 vertex;  
layout(location = 1) in vec4 color;  
out vec4 inFragColor;  
  
void main( void )  
{  
    gl_Position = vertex;  
    inFragColor = color;  
}  

片元Shader

#version 330  
in vec4 inFragColor;  
out vec4 outFragColor;  
  
void main( void )  
{  
    outFragColor = inFragColor;  
}  


經過頂點Shader能夠知道索引值爲0的VBO解釋爲頂點位置,它傳給gl_Position,索引值爲1的VBO解釋爲頂點的顏色

  • VAO API

VAO的介紹能夠參考 <<AB是一家?VAO與VBO>>

(1)初始化

//////////////////////////////////////////////////////////////////////////  
//初始化VAO  
  
void glGenVertexArrays(GLsizei n,  GLuint *arrays);  
//建立VAO ID  
//參數描述  
//n                 產生VAO ID的數量  
//arrays                    保存VAO ID的數組  
  
void glBindVertexArray(GLuint array);  
//設置當前操做的VAO對象  
//參數描述  
//array              VAO的ID  


(2)開啓/關閉VAO

VAO的開啓當使用glBindVertexArray時自動開啓,使用glBindVertexArray(0),傳入一個0值能夠視爲將VAO關閉

設置VAO的過程就是調用VBO中(2)的過程

(3) 繪製

同VBO中繪製(繪製以前先啓用VAO)

以上就是VAO涉及到的API,使用過程以下:

首先建立VAO,接着設置VAO(在調用VBO的數據設置過程當中VAO自動完成了設置),最後開啓VAO並繪製:

//////////////////////////////////////////////////////////////////////////  
GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,  
0.5f, 0.0f, 0.0f,  
0.0f, 0.5f, 0.0f };  
  
GLfloat vColors [] = { 1.0f, 0.0f, 0.0f, 1.0f,  
0.0f, 1.0f, 0.0f, 1.0f,  
0.0f, 0.0f, 1.0f, 1.0f };  
//////////////////////////////////////////////////////////////////////////  
  
void RenderWidget::paintGL()  
{  
    //////////////////////////////////////////////////////////////////////////  
    //生成VBO  
    glGenBuffers(1, &_vertexBuffer);  
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(vVerts), vVerts, GL_STATIC_DRAW);  
  
    glGenBuffers(1, &_colorBuffer);  
    glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(vColors), vColors, GL_STATIC_DRAW);  
  
    setShaders();  
  
      
    //初始化VAO  
    GLuint vaoBuffer;  
    glGenVertexArrays(1, &vaoBuffer);  
    glBindVertexArray(vaoBuffer);  
  
    //經過設置VBO裏面存儲的數據模式完成VAO設置  
    glEnableVertexAttribArray(0);  
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);  
    glVertexAttribPointer(  
        0,  
        3,  
        GL_FLOAT,  
        GL_FALSE,   
        0,  
        (void*)0  
        );  
    glEnableVertexAttribArray(1);  
    glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);  
    glVertexAttribPointer(  
        1,  
        4,  
        GL_FLOAT,  
        GL_FALSE,  
        0,  
        (void*)0  
        );  
    glBindVertexArray(0);  
    glBindBuffer(GL_ARRAY_BUFFER, 0);  
  
    //////////////////////////////////////////////////////////////////////////  
    //上述全部的代碼在初始化函數中調用  
    //只有繪製代碼在渲染函數中調用  
    //使用VAO繪製  
    glBindVertexArray(vaoBuffer);  
    glDrawArrays(GL_TRIANGLES, 0, 3);  
    glBindVertexArray(0);  
}
相關文章
相關標籤/搜索