yang131

导航

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);
View Code

第二种关联方式:则一个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);
}

 

posted on 2022-09-06 15:07  NoNight  阅读(266)  评论(0编辑  收藏  举报