尽可能少。切换 VBO 的成本虽小,但可衡量。通常,您会尝试将相似的数据分组到 VBO 中。例如在 FPS 游戏中,所有不同种类的街头垃圾、小道具等,通常会位于相同或仅少数几个 VBO 中。
这也归结为绘制批量大小。 glDraw… 渲染少于大约 100 个基元的调用是次优的(即使在 15 年前也是如此)。因此,您希望尽可能批处理至少 100 个基元。但是,如果单个网格只有 20 个三角形(用于实例化的低多边形道具等),每个都在其自己的 VBO 中,您不能再批量处理更多。
glGenBuffers → glBindBuffer → glBufferData
更新你可以传递一个空指针给glBufferData的data参数来初始化缓冲区对象而不需要设置数据。
- 如果数据大小不固定,我应该如何更新 vbos 数据?
创建比您的数据大小更粗的 VBO。无论如何,您的操作系统都会为您的主机端数据执行此操作,这称为分页。此外,如果您想使用 glMapBuffer 将缓冲区对象设置为主机页面大小的倍数,这对整个系统来说非常好。
当前系统上通常的页面大小是 4kiB。这就是我选择的 VBO 大小粒度。 更新:您可以顺便问一下您的操作系统它正在使用哪个页面大小。不过,这取决于操作系统,我会问另一个问题。
使用 glBufferSubData 更新数据或使用 glMapBuffer 在主机端映射内存中修改映射,然后使用 glUnmapBuffer。
如果数据超出缓冲区对象,则创建一个新的更大的对象并使用 glCopyBufferSubData 进行复制。见最后一段。
glBindBuffer → glDraw…
如果数据仅消耗 VBO 的一部分并与其他数据共享,并且您没有耗尽内存,那么,请不要访问它。理想情况下,您保留一些索引,您可以在其中跟踪哪个 VBO 的哪些部分可用于哪种任务。这很像内存管理,特别是一种称为对象堆栈(obstacks)的方案。
然而,最终压缩现有的缓冲区对象可能是有意义的。为此,您将创建一个新的缓冲区对象,将其绑定为写入目标,并选择旧的缓冲区对象作为读取目标。然后使用 glCopyBufferSubData 将内容复制到一个新的、紧缩的缓冲区对象中。当然,您必须更新所有对缓冲区对象名称(=OpenGL ID)和偏移量的引用。
因此,在 OpenGL 缓冲区对象之上编写一个薄抽象层是有意义的,它跟踪 OpenGL 缓冲区对象所在的无结构 blob 中的实际类型数据。