可能大家经常从别人口中听到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, //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;
}
运行结果,左边是不使用VBO的,右边是使用VBO的