顶点数组对象和顶点缓冲对象的使用

48
我正在尝试理解如何使用VAO和VBO以及它们之间的关系。假设我想创建一个简单的地形和一个有纹理的立方体,对于这两个对象,我都有三角形顶点数组,而对于立方体,我有一个包含纹理数据的数组。我的问题是:我该如何使用VAOs和VBOs创建和渲染这两个对象?
  1. 我需要为每个对象创建一个VAO和一个VBO吗?
  2. 或者我应该为每个对象的VBO(顶点、纹理数据等)创建一个VAO?
有很多教程和书籍,但我仍然不明白这些概念应该如何被理解和使用。
2个回答

83

基本上,您需要了解两件事:

  1. 顶点数组对象(VAO)在概念上只是薄状态包装。

  2. 顶点缓冲对象(VBO)存储实际数据。

另一种思考这个问题的方法是,VAOs描述存储在一个或多个VBO中的数据。

将VBOs(和缓冲区对象一般)视为存储在服务器(GPU)内存中的非结构化数据数组。如果您想要,可以将顶点数据布局在多个数组中,或者将它们压缩到单个数组中。在任何情况下,缓冲区对象都会转化为存储数据的位置。

顶点数组对象跟踪绘制命令所需的指向VBO内存的实际指针。

然而,与C语言中的指针相比,它们略微复杂一些。顶点指针跟踪了绑定时指定的缓冲对象、其地址空间中的偏移量、顶点属性之间的步幅以及如何解释底层数据(例如,将整数值保持不变还是将其转换为浮点型[0.0,1.0],通过将其归一化到数据类型的范围)。

例如,整数数据通常会被转换为浮点型,但是您用于指定顶点指针的命令(glVertexAttribPointer (...)glVertexAttribIPointer (...))决定了此行为。

顶点数组对象还跟踪当前绑定到GL_ELEMENT_ARRAY_BUFFER的缓冲对象。

GL_ELEMENT_ARRAY_BUFFERglDrawElements (...) 命令从中获取其索引列表的地方(假设绑定非零),并且 没有 glElementArrayPointer (...) 命令。 glDrawElements (...) 将指针和绘制命令组合成单个操作,并将使用存储在活动顶点数组对象中的绑定来完成此操作。


有了这个前提,除非您的对象共享顶点数据,否则通常需要为每个对象使用唯一的VBO集。

如果您愿意,可以为整个软件使用单个VAO,或者您可以利用改变绑定VAO会改变几乎所有必要状态以绘制不同对象的事实。

因此,绘制您的地形和立方体可能只需更改绑定的VAO即可。如果您需要为它们应用不同的纹理,则可能需要执行更多操作,但是VAO会处理所有与顶点数据相关的设置。


4
非常感谢。我现在确切地了解了VAO和VBO是什么。看起来我必须对我想要创建的3D对象有一个好的清晰概述,然后选择最佳的使用VAOs和VBOs以节省内存和CPU/GPU时间的方法。 - ali

8
您的问题不容易在这里得到答复,而是需要通过教程来学习。您可能已经了解了这两个网站,但如果没有的话,我会留下参考文献。 OGLDEV OpenGL-Tutorial.org 现在,为了阐明你的问题,一个顶点数组对象是一个OpenGL对象,旨在减少绘制调用的API开销。您可以把它想象成一个包含顶点缓冲区及其相关状态的容器。或许类似于旧版显示列表。
通常,VAO和VBO之间存在一对一的关系;也就是说,每个VAO包含唯一的VBO。但这并非必须如此。您可以有多个VAO引用同一个VBO。
我认为,在代码中建模这个最简单的方法是,你可以有一个VAO类/类型以及一个将VBO附加到它的方法。然后为每个mesh提供一个VAO实例。mesh反过来可以引用自己的VBO类型或共享的类型。

1
谢谢。我已经访问了那些链接,但现在我更加理解了,所以我会再去看一眼。 - ali
为每个VBO拥有一个VAO将非常低效,因为你需要在每次绘制调用之前绑定一个VAO。 - Stradigos
@Stradigos,我不认为这比在每次绘制调用之前绑定VBO + IBO更低效。使用VAO,您需要更少的API调用来设置绘图上下文。 - glampert
个人资料,当然,但请参阅此线程:https://dev59.com/Z2Ml5IYBdhLWcg3wV1wE 编辑:我很确定我之前在OpenGL的网站上读到过。不过现在找不到了。 - Stradigos
1
@Stradigos,从您提供的答案链接中可以看出:"个人而言,我倾向于每个布局使用一个VBO和VAO,也就是说,如果我的数据由具有相同属性的相等数量的属性组成,我将它们放入单个VBO和单个VAO中。" - 这正是我会这样做的方式;) 如果我的回答给您带来了不同的理解,请接受我的道歉。 - glampert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接