OpenGL 理解VAO VBO 和 IBO

曾经有一个老师在教我们的时候,给我们说了一句很实在的话,当你接触一个新的领域的时候,什么最压人?名词最压人,随便一个专有名词都够你吃一壶的。事实上确实如此。

学习OpenGL首先,先要弄明白的三个名词就是VAO,VBO和IBO。我们学习一个东西的时候,往往被过多,过详细的数据,淹没真正核心的那几句话,这可能也是不同人学习能力不一样的原因把,我应该属于学习能力比较弱的那种,总是要一边叮嘱自己,关键那几句话在哪?一边看东西,才能勉强找到关键那几句话。

本篇不是教程,不会说明这三个玩意的来龙去脉以及用法,只是在你已具备相应基础的前提下,加深一下认知。

VAO:顶点数组对象,Vertex Array Object

VBO:顶点缓存对象,Vertex Buffer Object,VBO

IBO:顶点索引对象,Element Buffer Object,EBO或Index Buffer Object,IBO


借用一下LearnOpenGL网站上的图。

最让我迷惑的不是IBO,而是VAO的作用,其实这VAO和IBO一样,都是为了“复用”。而VBO关键作用在于“打包”上传。

从上图看来,VAO,其实是VBO的约束器,解读器。它管理一类VBO的配置。VAO1,只用到一个属性,VAO2则有两个属性,位置和颜色。同时还要用AttributePointer去描述VBO。

1、VAO

顶点数组对象:

使用VAO的好处——复用属性设置。

1、VAO是可复用的

2、VAO可以绑定一系列顶点属性

3、VAO只能和匹配的VBO绑定,比如你VAO只描述了位置,而VBO包含了顶点和颜色信息,他们不能绑定,否则数据就错乱了

4、VAO解绑后可以绑定给其他匹配的VBO,这就是“复用”。

2、VBO

顶点缓存对象:

使用VBO的好处——批量数据传输。

具体来讲:使用VBO好处是可以一次发送大量顶点到显卡,而不是每次只发送一个顶点。

这里额外思考一下,既然IO是相当费时的,那么如果内存和显卡可以异步传输数据是不是能节省掉一部分时间?比如先传A数据,然后渲染A的同时传B的树呢?这应该就是多线程渲染做的事情,并不是真的多线程渲染,而是把渲染和IO交错以节省时间。当然如果瓶颈不是IO,而是渲染本事,那多线程渲染也无力回天了。

3、IBO

先对来讲IBO是最好理解的,假定一个四边形由两个三角形组成,共六个顶点,实际上有两对顶点共享,所以只有四个顶点,IBO用来告诉你,如何复用这些顶点。

比如0,1,3是一个三角形,1,2,3是另一个三角形。

这就是IBO的复用。

依据以上总结,在回头来看这段源代码,就思路就相对清晰了。

//声明顶点数据,实际应用的时候,应该是从FBX等文件里面读取的。
//很少自己生成
float vertices[] = {
    -0.5f, -0.5f, 0.0f, // left  
    0.5f, -0.5f, 0.0f, // right 
    0.0f,  0.5f, 0.0f  // top   
};
 
//创建顶点数组对象和顶点缓存对象
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
 
//绑定,或者说“选用”这个顶点缓存对象。
glBindVertexArray(VAO);	
 
//绑定VBO对象,并拷贝数据到VBO
//注意数据不是拷贝给VAO的,VAO里面不存顶点数据
//GL_STATIC_DRAW,这是不是Unity区分Static和non-static物件的原因?
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), 
             vertices, GL_STATIC_DRAW);
 
//描述顶点,并把它在0号位置激活,一个VAO可以有多个描述对象。
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
 
//相当于解除绑定。注意是解绑,不是删除。
glBindBuffer(GL_ARRAY_BUFFER, 0);
//简单来讲,按照绑定的反顺序进行解绑就对了。先解绑VBO,再解绑VAO
glBindVertexArray(0);
//到此位置,该送入GPU的数据以及都“送”进去了
 
//开始进入渲染主循环
while (!glfwWindowShouldClose(window))
{
    //处理输入
    processInput(window);
    //清屏
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    //选择shader,这里不是本篇要讲的内容,略过
    glUseProgram(shaderProgram);
    //因为没有其他数据,所以不用绑定解绑也没事,这么做是为了规范。
    //思考:当VBO有多个的时候,这里要怎么做?
    glBindVertexArray(VAO); 
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glBindVertexArray(0);
    
    glfwSwapBuffers(window);
    glfwPollEvents();
}
 
//这里才是删除缓存
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);    

版权声明:本文为CSDN博主「幼发拉底河上的无头女尸」的原创文章,
遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/WangHaoDiablo/article/details/104407245

最新文章