我试图了解这两者,如何使用它们以及它们之间的关系。假设我想创建一个简单的地形和一个带纹理的立方体。对于这两个对象,我都有三角形顶点数组,对于立方体,我有一个包含纹理数据的数组。我的问题是:如何使用 VAO 和 VBO 来创建和渲染这两个?
- 我是否必须为每个对象创建一个 VAO 和 VBO?
- 还是应该为每个对象的 VBO(顶点、纹理数据等)创建一个 VAO?
有很多教程和书籍,但我仍然不明白如何理解和使用这些概念。
顶点数组对象(VAO)在概念上只不过是薄状态包装器。
顶点缓冲区对象 (VBO) 存储实际数据。
另一种思考方式是 VAO 描述存储在一个或多个 VBO 中的数据。
将 VBO(以及一般的缓冲区对象)视为存储在服务器 (GPU) 内存中的非结构化数据数组。如果需要,您可以将顶点数据布局在多个数组中,也可以将它们打包到单个数组中。无论哪种情况,缓冲区对象都归结为您将存储数据的位置。
顶点数组对象跟踪绘制命令所需的指向 VBO 内存的实际指针。
但是,它们比指针更复杂一点,就像您在 C 等语言中所知道的那样。顶点指针跟踪指定它们时绑定的缓冲区对象、到其地址空间的偏移量、顶点属性之间的跨度以及如何解释基础数据(例如,是否保留整数值或将它们转换为浮点数 [ 0.0 , 1.0 ] 通过规范化到数据类型的范围)。
例如,整数数据通常会转换为浮点数,但正是您用来指定顶点指针 ( glVertexAttribPointer (...)
vs. glVertexAttribIPointer (...)
) 的命令决定了这种行为。
顶点数组对象还跟踪当前绑定到的缓冲区对象GL_ELEMENT_ARRAY_BUFFER
。
GL_ELEMENT_ARRAY_BUFFER
是命令的位置:glDrawElements (...)
从(假设非零绑定)获取其索引列表并且没有 glElementArrayPointer (...)
命令。glDrawElements (...)
将指针和绘图命令组合成一个操作,并将使用存储在活动顶点数组对象中的绑定来完成此操作。
除此之外,除非您的对象共享顶点数据,否则您通常需要为每个对象提供一组唯一的 VBO。
如果需要,您可以为整个软件使用单个 VAO,或者您可以利用这样一个事实,即更改绑定的 VAO 几乎会更改绘制不同对象所需的整个状态集。
因此,绘制地形和立方体就像更改绑定的 VAO 一样简单。如果您需要为每个纹理应用不同的纹理,您可能需要做更多的事情,但 VAO 会负责所有与顶点数据相关的设置。
您的问题在这里不容易回答,而是在教程中回答。您可能已经知道这两个网站,但如果不知道,我将留下参考资料。
现在试图阐明您的问题,顶点数组对象是一个 OpenGL 对象,旨在减少绘制调用的 API 开销。您可以将其视为 Vertex Buffer 及其关联状态的容器。可能类似于旧的显示列表。通常,VAO 和 VBO 之间存在 1 对 1 的关系;也就是说,每个 VAO 都包含一个唯一的 VBO。但这并不是绝对必要的。您可以有多个 VAO 引用相同的 VBO。
我认为,在代码中对此进行建模的最简单方法是让您拥有一个 VAO 类/类型和一个将 VBO 附加到它的方法。然后为每个网格提供一个 VAO 实例。网格反过来可以引用一个 VBO 类型,该类型可能是它自己的或共享的。