可能大家经常从别人口中听到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来绘制图元,使用顶点数组-结构数组; 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, //v0 1.0f,0.0f,0.0f,1.0f, //c0 -1.0f,-0.5f,0.0f, //v1 0.0f,1.0f,0.0f,1.0f, //c1 0.0f,-0.5f,0.0f, //v2 0.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 1 typedef 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, //v0 1.0f,0.0f,0.0f,1.0f, //c0 -1.0f,-0.5f,0.0f, //v1 0.0f,1.0f,0.0f,1.0f, //c1 0.0f,-0.5f,0.0f, //v2 0.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; }