OpenGL 4.0中数据缓冲VBO,VAO,EBO的使用总结
Opengl是大家常用的一个API,我们用它绘制数据的时候需要使用vao,vbo,ebo等对象,绘制方式分为 vao绘制,ebo绘制等。使用不同api还能分为普通调用以及Instance绘制。
首先申请vao,vbo和以及他们两者的绑定:
这里注意的是,百度百科上说的最多支持四个位置,我测试了一下,7到8个顶点属性都没问题,比如Position,Texcoord,Color,Normal等等,需要排列好。
其实他们的绑定有两种方式,第一种是vao搭配多个vbo,每一个vbo代表顶点的一种属性,比如vbo[0] 代表位置,vbo[1]代表纹理坐标。。代码如下:
//位置 glGenBuffers(1, &vbo0); glBindBuffer(GL_ARRAY_BUFFER, vbo0); glBufferData(GL_ARRAY_BUFFER, 3 * 4 * sizeof(GLfloat), posCube, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), nullptr); //纹理 glGenBuffers(1, &vbo1); glBindBuffer(GL_ARRAY_BUFFER, vbo1); glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(GLfloat), texCube, GL_STATIC_DRAW); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), nullptr); //法线 glGenBuffers(1, &vbo2); glBindBuffer(GL_ARRAY_BUFFER, vbo2); glBufferData(GL_ARRAY_BUFFER, 3 * 4 * sizeof(GLfloat), normalCube, GL_STATIC_DRAW); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 3, GL_FLOAT, GL_TRUE, 3 * sizeof(GLfloat), nullptr); //法线 glGenBuffers(1, &vbo3); glBindBuffer(GL_ARRAY_BUFFER, vbo3); glBufferData(GL_ARRAY_BUFFER, 3 * 4 * sizeof(GLfloat), normalCube2, GL_STATIC_DRAW); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 3, GL_FLOAT, GL_TRUE, 3 * sizeof(GLfloat), nullptr); //法线 glGenBuffers(1, &vbo4); glBindBuffer(GL_ARRAY_BUFFER, vbo4); glBufferData(GL_ARRAY_BUFFER, 3 * 4 * sizeof(GLfloat), normalCube3, GL_STATIC_DRAW); glEnableVertexAttribArray(4); glVertexAttribPointer(4, 3, GL_FLOAT, GL_TRUE, 3 * sizeof(GLfloat), nullptr); //法线 glGenBuffers(1, &vbo5); glBindBuffer(GL_ARRAY_BUFFER, vbo5); glBufferData(GL_ARRAY_BUFFER, 3 * 4 * sizeof(GLfloat), normalCube4, GL_STATIC_DRAW); glEnableVertexAttribArray(5); glVertexAttribPointer(5, 3, GL_FLOAT, GL_TRUE, 3 * sizeof(GLfloat), nullptr);
第二种关联方式:则一个vao搭配一个vbo,这种传递方式类似于把结构体数组或者内存的数据传递给vbo。 内存排布 是这样的:位置纹理法线位置纹理法线 。位置是float x3,纹理floatx2,法线float x3,
解析的时候就是 顶点1: 位置纹理法线 顶点2位置纹理法线,他们会有间隔
struct MyVertex { vec3 pos; vec2 tex; vec3 normal; }; //顶点定义 for (int i = 0; i < 4; i++) { MyVertex v; v.pos.x = posCube[i * 3 + 0]; v.pos.y = posCube[i * 3 + 1]; v.pos.z = posCube[i * 3 + 2]; v.tex.s = texCube[i * 2 + 0]; v.tex.t = texCube[i * 2 + 1]; v.normal.x = normalCube2[i * 3 + 0]; v.normal.y = normalCube2[i * 3 + 1]; v.normal.z = normalCube2[i * 3 + 2]; eboBuffers.push_back(v); } glGenVertexArrays(1, &vao); glBindVertexArray(vao); glGenBuffers(1, &vbo0); glBindBuffer(GL_ARRAY_BUFFER, vbo0); glBufferData(GL_ARRAY_BUFFER, eboBuffers.size()* sizeof(MyVertex), eboBuffers.data(), GL_STATIC_DRAW); int stride = sizeof(MyVertex); glEnableVertexAttribArray(0); //根据属性数量来顶,这里开启三个 0 1 2 glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)offsetof(MyVertex, pos)); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)offsetof(MyVertex, tex)); glVertexAttribPointer(2, 3, GL_FLOAT, GL_TRUE, stride, (GLvoid*)offsetof(MyVertex, normal));
VAO绘制:
glBindVertexArray(vao); glDrawArrays(GL_QUADS, first, count); //第一个是起始位置,第二个是顶点数量
EBO的绘制:
ebo的好处是可以省大量的内存,只需要调用顶点所在的索引(id)即可完成绘制,因为
在有了vao,vbo的申请以及他们的内存关联之后,ebo开始工作。
ebo绘制有两种方式
1)先申请在绘制
//申请: glGenBuffers(1, &eboId); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId); glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(GLubyte), vIdx, GL_STATIC_DRAW); //vidx 即绘制索引数组 GLubyte vIdx[] = { 0,1,2,1,2,3 }; //绘制: glBindVertexArray(vao); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId); glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, 0); //最后一个参数是0,因为已经绑定了vao,ebo
2. 直接使用数组
//清除已绑定的ebo glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindVertexArray(vao); glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, vIdx); //传入索引数组内存以及顶点数量
最后绘制在Shader中的展示(ebo,vbo调用均一样): Normal2,Normal3,Normal4 是用来测试顶点属性数量用的,支持6个属性没问题
uniform mat4 proj; uniform mat4 view; uniform mat4 model; layout (location = 0) in vec3 Position; //layout (locatiopn=0)一定要写,否则识别会出问题 layout (location = 1) in vec2 Texcoord; layout (location = 2) in vec3 Normal; layout (location = 3) in vec3 Normal2; layout (location = 4) in vec3 Normal3; layout (location = 5) in vec3 Normal4; out vec3 Color; void main() { Color = Normal4; gl_Position = proj * view *model* vec4(Position,1); }