GLES 頂點緩衝區對象(VBO),glesvbo
可能大家經常從別人口中聽到VBO,不知道是什麼意思,覺得高大上的樣子,但是如果知道中文名稱,應該能明白一二。
VBO,即頂點緩衝區對象。
使用頂點數組時,指定的頂點資料儲存在系統記憶體中,在進行glDrawArrays 或者glDrawElements 的時候,需要把這些頂點資料複製到顯卡記憶體。
很麻煩。其實我們想想,直接把頂點資料儲存在顯卡記憶體中,這樣不是就免去了複製這一步操作。這種方法可以改進渲染效能,而且降低了記憶體頻寬消耗。
在使用VBO之前,我們需要申請分配緩衝區對象,並且將頂點資料和元素索引上傳到對應的緩衝區對象。
下面的例子來說明使用方式:
//建立和綁定頂點緩衝區對象(VBO);void InitVertexBufferObjects(float* vertexBuffer,GLushort* indices,GLuint numVertices,GLuint numIndices,GLuint* vboIds){glGenBuffers(2,vboIds);//申請兩塊緩衝區,一個用於儲存實際頂點資料,一個用於儲存組成圖元的元素索引;glBindBuffer(GL_ARRAY_BUFFER,vboIds[0]); //指定當前緩衝區對象;glBufferData(GL_ARRAY_BUFFER,numVertices*sizeof(float),vertexBuffer,GL_STATIC_DRAW);//建立和初始化資料存放區;glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,vboIds[1]);glBufferData(GL_ELEMENT_ARRAY_BUFFER,numIndices*sizeof(float),indices,GL_STATIC_DRAW);}
下面來看看,不使用VBO和使用VBO 進行圖元繪製的不同操作:
//不使用VBO來繪製圖元,使用頂點數組-結構數組;void DrawPrimitiveWithoutVBOs(GLfloat* vertices,GLint vtxStride,GLint numIndices,GLushort* indices){GLfloat *vertexBuffer=vertices;glBindBuffer(GL_ARRAY_BUFFER,0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);glEnableVertexAttribArray(VERTEX_POS_INDX);//允許頂點數組;glEnableVertexAttribArray(VERTEX_COLOR_INDX);glVertexAttribPointer(VERTEX_POS_INDX,VERTEX_POS_SIZE,GL_FLOAT,GL_FALSE,vtxStride,vertexBuffer);//頂點數組;vertexBuffer=vertexBuffer+VERTEX_POS_SIZE; //位移;glVertexAttribPointer(VERTEX_COLOR_INDX,VERTEX_COLOR_SIZE,GL_FLOAT,GL_FALSE,vtxStride,vertexBuffer);glDrawElements(GL_TRIANGLES,numIndices,GL_UNSIGNED_SHORT,indices);glDisableVertexAttribArray(VERTEX_POS_INDX);glDisableVertexAttribArray(VERTEX_COLOR_INDX);}//使用VBO來繪製圖元;void DrawPrimitiveWithVBOs(ESContext* esContext,GLint numVertices,GLfloat* vertexBuffer,GLint vtxStride,GLint numIndices,GLushort* indices){UserData* userData=(UserData*)esContext->userData;GLuint offset=0;if(userData->vboIds[0]==0 && userData->vboIds[1]==0){glGenBuffers(2,userData->vboIds);glBindBuffer(GL_ARRAY_BUFFER,userData->vboIds[0]);glBufferData(GL_ARRAY_BUFFER,vtxStride*numVertices,vertexBuffer,GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,userData->vboIds[1]);glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(GLushort)*numIndices,vertexBuffer,GL_STATIC_DRAW);}glBindBuffer(GL_ARRAY_BUFFER,userData->vboIds[0]);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,userData->vboIds[1]);glEnableVertexAttribArray(VERTEX_POS_INDX);glEnableVertexAttribArray(VERTEX_COLOR_INDX);//注意開始使用VBO,最後的參數不再是vertexBuffer,而是VBO中的資料;glVertexAttribPointer(VERTEX_POS_INDX,VERTEX_POS_SIZE,GL_FLOAT,GL_FALSE,vtxStride,(const void*)offset);offset =offset+VERTEX_POS_SIZE*sizeof(float); //位移;glVertexAttribPointer(VERTEX_COLOR_INDX,VERTEX_COLOR_SIZE,GL_FLOAT,GL_FALSE,vtxStride,(const void*)offset);glDisableVertexAttribArray(VERTEX_POS_INDX);glDisableVertexAttribArray(VERTEX_COLOR_INDX);//恢複預設綁定VBO;glBindBuffer(GL_ARRAY_BUFFER,0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);}void Draw(ESContext* esContext){UserData *userData=(UserData*)esContext->userData;GLfloat vertices[3*(VERTEX_POS_SIZE+VERTEX_COLOR_SIZE)]={-0.5f,0.5f,0.0f,//v01.0f,0.0f,0.0f,1.0f,//c0-1.0f,-0.5f,0.0f,//v10.0f,1.0f,0.0f,1.0f,//c10.0f,-0.5f,0.0f,//v20.0f,0.0f,1.0f,1.0f,//c2};GLushort indices[3]={0,1,2};glViewport(0,0,esContext->width,esContext->height);glClear(GL_COLOR_BUFFER_BIT);glUseProgram(userData->programObject);glUniform1f(userData->offsetLoc,0.0f);DrawPrimitiveWithoutVBOs(vertices,sizeof(GLfloat)*(VERTEX_POS_SIZE+VERTEX_COLOR_SIZE),3,indices);glUniform1f(userData->offsetLoc,1.0f);DrawPrimitiveWithVBOs(esContext,3,vertices,sizeof(GLfloat)*(VERTEX_POS_SIZE+VERTEX_COLOR_SIZE),3,indices);}
整個代碼
#include "esUtil.h"#define VERTEX_POS_SIZE 3#define VERTEX_COLOR_SIZE 4#define VERTEX_POS_INDX 0#define VERTEX_COLOR_INDX 1typedef struct {GLuint programObject; //儲存GLProgram;GLuint vboIds[2];//vbo對象;GLuint offsetLoc;} UserData;//載入Shader;GLuint LoadShader(GLenum type,const char*shaderSrc){GLuint shader;GLint compiled;shader=glCreateShader(type);if (shader==0){return 0;}glShaderSource(shader,1,&shaderSrc,NULL);glCompileShader(shader);glGetShaderiv(shader,GL_COMPILE_STATUS,&compiled);if (!compiled){GLint infoLen=0;glGetShaderiv(shader,GL_INFO_LOG_LENGTH,&infoLen);if (infoLen>1){char* infoLog=(char*)malloc(sizeof(char) * infoLen);glGetShaderInfoLog(shader,infoLen,NULL,infoLog);esLogMessage("Error Compiling Shader : %s",infoLog);free(infoLog);}glDeleteShader(shader);return 0;}return shader;}//初始化Shader和GLProgram;int Init(ESContext *esContext){UserData *userData=(UserData*)esContext->userData;char vShaderStr[]="#version 300 es\n""layout(location = 0) in vec4 a_Position;"//指定頂點屬性的索引,可選,如果沒有設定程式將自動分配;"layout(location = 1) in vec4 a_Color;""uniform float u_offset;""out vec4 v_Color;" //輸出值到Fragment Shader;平面著色;"void main()""{""v_Color=a_Color;""gl_Position=a_Position;""gl_Position.x += u_offset;""}";char fShaderStr[]="#version 300 es\n""precision mediump float;"//預設精度限定符;還有highp,lowp,mediump;"in vec4 v_Color;" //來自Vertex Shader的值;"out vec4 o_fragColor;""void main()""{""o_fragColor=vec4(v_Color);""}";GLuint vertexShader;GLuint fragmentShader;GLuint programObject;GLint linked;vertexShader=LoadShader(GL_VERTEX_SHADER,vShaderStr);fragmentShader=LoadShader(GL_FRAGMENT_SHADER,fShaderStr);programObject=glCreateProgram();if(programObject==0){return 0;}glAttachShader(programObject,vertexShader);glAttachShader(programObject,fragmentShader);glLinkProgram(programObject);glGetProgramiv(programObject,GL_LINK_STATUS,&linked);if(!linked){GLint infoLen=0;glGetProgramiv(programObject,GL_INFO_LOG_LENGTH,&infoLen);if(infoLen>1){char* infoLog=(char*)malloc(sizeof(char)*infoLen);glGetProgramInfoLog(programObject,infoLen,NULL,infoLog);esLogMessage("Error linking program : %s \n",infoLog);free(infoLog);}glDeleteProgram(programObject);return 0;}userData->programObject=programObject;userData->offsetLoc=glGetUniformLocation(programObject,"u_offset");userData->vboIds[0]=0;userData->vboIds[1]=0;glClearColor(1.0f,1.0f,1.0f,1.0f);return 1;}//建立和綁定頂點緩衝區對象(VBO);void InitVertexBufferObjects(float* vertexBuffer,GLushort* indices,GLuint numVertices,GLuint numIndices,GLuint* vboIds){glGenBuffers(2,vboIds);//申請兩塊緩衝區,一個用於儲存實際頂點資料,一個用於儲存組成圖元的元素索引;glBindBuffer(GL_ARRAY_BUFFER,vboIds[0]); //指定當前緩衝區對象;glBufferData(GL_ARRAY_BUFFER,numVertices*sizeof(float),vertexBuffer,GL_STATIC_DRAW);//建立和初始化資料存放區;glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,vboIds[1]);glBufferData(GL_ELEMENT_ARRAY_BUFFER,numIndices*sizeof(float),indices,GL_STATIC_DRAW);}//不使用VBO來繪製圖元,使用頂點數組-結構數組;void DrawPrimitiveWithoutVBOs(GLfloat* vertices,GLint vtxStride,GLint numIndices,GLushort* indices){GLfloat *vertexBuffer=vertices;glBindBuffer(GL_ARRAY_BUFFER,0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);glEnableVertexAttribArray(VERTEX_POS_INDX);//允許頂點數組;glEnableVertexAttribArray(VERTEX_COLOR_INDX);glVertexAttribPointer(VERTEX_POS_INDX,VERTEX_POS_SIZE,GL_FLOAT,GL_FALSE,vtxStride,vertexBuffer);//頂點數組;vertexBuffer=vertexBuffer+VERTEX_POS_SIZE; //位移;glVertexAttribPointer(VERTEX_COLOR_INDX,VERTEX_COLOR_SIZE,GL_FLOAT,GL_FALSE,vtxStride,vertexBuffer);glDrawElements(GL_TRIANGLES,numIndices,GL_UNSIGNED_SHORT,indices);glDisableVertexAttribArray(VERTEX_POS_INDX);glDisableVertexAttribArray(VERTEX_COLOR_INDX);}//使用VBO來繪製圖元;void DrawPrimitiveWithVBOs(ESContext* esContext,GLint numVertices,GLfloat* vertexBuffer,GLint vtxStride,GLint numIndices,GLushort* indices){UserData* userData=(UserData*)esContext->userData;GLuint offset=0;if(userData->vboIds[0]==0 && userData->vboIds[1]==0){glGenBuffers(2,userData->vboIds);glBindBuffer(GL_ARRAY_BUFFER,userData->vboIds[0]);glBufferData(GL_ARRAY_BUFFER,vtxStride*numVertices,vertexBuffer,GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,userData->vboIds[1]);glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(GLushort)*numIndices,indices,GL_STATIC_DRAW);}glBindBuffer(GL_ARRAY_BUFFER,userData->vboIds[0]);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,userData->vboIds[1]);glEnableVertexAttribArray(VERTEX_POS_INDX);glEnableVertexAttribArray(VERTEX_COLOR_INDX);//注意開始使用VBO,最後的參數不再是vertexBuffer,而是VBO中的資料;glVertexAttribPointer(VERTEX_POS_INDX,VERTEX_POS_SIZE,GL_FLOAT,GL_FALSE,vtxStride,(const void*)offset);offset =offset+VERTEX_POS_SIZE*sizeof(float); //位移;glVertexAttribPointer(VERTEX_COLOR_INDX,VERTEX_COLOR_SIZE,GL_FLOAT,GL_FALSE,vtxStride,(const void*)offset);glDrawElements(GL_TRIANGLES,numIndices,GL_UNSIGNED_SHORT,0);glDisableVertexAttribArray(VERTEX_POS_INDX);glDisableVertexAttribArray(VERTEX_COLOR_INDX);//恢複預設綁定VBO;glBindBuffer(GL_ARRAY_BUFFER,0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);}void Draw(ESContext* esContext){UserData *userData=(UserData*)esContext->userData;GLfloat vertices[3*(VERTEX_POS_SIZE+VERTEX_COLOR_SIZE)]={-0.5f,0.5f,0.0f,//v01.0f,0.0f,0.0f,1.0f,//c0-1.0f,-0.5f,0.0f,//v10.0f,1.0f,0.0f,1.0f,//c10.0f,-0.5f,0.0f,//v20.0f,0.0f,1.0f,1.0f,//c2};GLushort indices[3]={0,1,2};glViewport(0,0,esContext->width,esContext->height);glClear(GL_COLOR_BUFFER_BIT);glUseProgram(userData->programObject);glUniform1f(userData->offsetLoc,0.0f);DrawPrimitiveWithoutVBOs(vertices,sizeof(GLfloat)*(VERTEX_POS_SIZE+VERTEX_COLOR_SIZE),3,indices);glUniform1f(userData->offsetLoc,1.0f);DrawPrimitiveWithVBOs(esContext,3,vertices,sizeof(GLfloat)*(VERTEX_POS_SIZE+VERTEX_COLOR_SIZE),3,indices);}void Draw1(ESContext* esContext){UserData *userData=(UserData*)esContext->userData;GLfloat vVertices[]={0.0f,0.5f,0.0f,-0.5f,-0.5f,0.0f,0.5f,-0.5f,0.0f};glViewport(0,0,esContext->width,esContext->height);glClear(GL_COLOR_BUFFER_BIT);glUseProgram(userData->programObject);glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,vVertices); //頂點緩衝區0;glEnableVertexAttribArray(0);//生效頂點緩衝區0;glDrawArrays(GL_TRIANGLES,0,3);}void ShutDown(ESContext* esContext){UserData *userData=(UserData*)esContext->userData;glDeleteProgram(userData->programObject);}int esMain(ESContext* esContext){EGLint majorVersion;//主要版本;EGLint minorVersion;//小版本;EGLDisplay display=eglGetDisplay(EGL_DEFAULT_DISPLAY);if(display==EGL_NO_DISPLAY){esLogMessage("no display");return 0;}if(!eglInitialize(display,&majorVersion,&minorVersion)){esLogMessage("eglInitialize error");return 0;}esContext->userData=malloc(sizeof(UserData));esCreateWindow(esContext,"Hello Triangle",960,640,ES_WINDOW_RGB);if(!Init(esContext)){return GL_FALSE;}esRegisterShutdownFunc(esContext,ShutDown);esRegisterDrawFunc(esContext,Draw);return GL_TRUE;}
運行結果,左邊是不使用VBO的,右邊是使用VBO的