有多少VAO和VBO

Sor*_*ona 4 language-agnostic opengl vbo vao

据我所知:VAO代表某种状态.如果我绑定一个VAO,添加一些VBO和元素缓冲区用于指示和东西我可以保存我想要绘制和激活的对象的某个状态,并在我想要渲染内容时稍后在路上绘制它们.对?

因此,VBO保存实际数据,而VAO只是一个"包装器对象",它保存指向我为其定义的所有缓冲区的指针?

改变VAO是昂贵的(正如改变VBO一样?!).目前我加载Meshes并将它们组合到模型中.每个模型使用它自己的VAO并且具有VBO(带有顶点)和带有索引的元素缓冲区.

现在我理解这一点,这是废话,因为我的世界中的每个对象(有一个模型)都使用它自己的VAO.对于我这个世界中的~30个对象来说不是问题,但是我想要做到这一点,并且将来可能会有数百或数千个对象,然后性能会大幅下降.

因此,就其模型而言,许多对象是"相同的".我的意思是,例如,如果你在世界上有某种树型,你可能会多次使用相同的模型,只是在不同的位置.

现在我该怎么做呢?我应该手动跟踪我的VAO,如:(伪代码跟随!)

treesVAOId = 1;
rabbitsVAOId = 2;
Run Code Online (Sandbox Code Playgroud)

然后,如果我加载一个模型,只需检查ID是否已被绑定(如何?)并在那里添加另一组VBO(甚至添加到正确的VBO?如果是这样,怎么样?)

我在想这里的大型比赛.让我们假设游戏中有数千个角色.当然不是所有这些都是同时渲染的,但我无法为它们中的每一个创建VAO和VBO,是吗?

我觉得有更多的部分缺失...就像实例化和再次使用(或多或少)相同的数据为不同的目的有效.

这是我缺少的一步,如何在现实世界中真正优化VAO和VBO的使用.

Ser*_*gey 8

您在此处描述的内容称为资源管理器资源管理器的至少一部分.描述外部文件中的资源是一种很好的做法,因此您需要一个资源文件,其中所有网格都以某种方式描述(考虑使用XML或JSON).

类层次结构

以下是类层次结构的可能方法:

每个VAO代表一个网格,定义它的顶点坐标,纹理坐标,法线,顶点颜色等.我认为在你有一个非常特殊的可视化案例之前,没有理由在几个VAO中使用相同的VBO.因此,假设您只使用每组数据一次,即使用VAO的类不应该知道有关底层VBO的任何内容,并且没有必要为VBO编写类包装器.

一组网格(可能只包含一个网格)代表一个模型.最小模型类应包括VAO(s)的句柄和几何变换信息(旋转,平移,无论你想要什么).为什么不是每个模型严格一个网格?有时您可能需要将一个变换应用于一组网格,其中有一个网格具有自己的模型 - 局部变换.例如,这样的构图可以用于一种骨架动画,或者仅用于从可能的武器库中获取具有任意武器的角色.此外,您可以将这些模型组合在一起,使用相同的界面获得更复杂的模型,这样您就可以获得相似的场景图.无论如何,将复合模式用于模型类是个好主意.

场景应包括模型,光源,力场等的集合.

资源经理

但是场景(或类似的游戏对象)会从哪里得到它的模型?资源经理应该回答这个问题.使每种模型由某种唯一标识符定义.在最简单的情况下,实际或虚拟文件系统中的路径可以被视为标识符,但它不是非常灵活.在我看来,最好使用富有表现力的人类可读名称在资源文件中定义所有网格,并将每个名称绑定到数据集(所有类型的坐标,颜色等)和属性.您的所有代码都不应该直接使用模型,但它应该使用资源管理器为您提供的句柄.显然,资源管理器必须在程序执行期间以及来自不同位置的调用之间保持状态.它旨在跟踪已存储在内存中的网格,并保留所有存储网格的VAO标识符.考虑使用单例模式作为资源管理器.

例:

ModelHandle footman = resMan->getModel("footman.model");
//.....
footman->setLocation(x,y,z);
footman->draw();
Run Code Online (Sandbox Code Playgroud)

这里对getModel("footman.model")的调用开始构造模型,导致调用

MeshHandle resMan->getMesh("footman1.mesh");
Run Code Online (Sandbox Code Playgroud)

获得所有网格.而getMesh做这项工作使这一切的解释.它检查之前是否加载了这样的网格,如果是,它只返回包含该网格的VAO的句柄.否则,它会创建新的VAO对象,将请求的数据加载到其中,并将句柄返回给新创建的对象.此对象的所有后续请求都不会导致新的VAO分配.

当然,所描述的场景图组织只是它应该看起来的粗略近似.例如,它没有区分模型和抽象场景图节点,但是为您的引擎开发和微调这种层次结构取决于您.

资源管理器类的最终接口是另一个讨论和设计的主题.一些问题和想法:

  • 您是否会使用单例或者您出于某种原因决定使用全局变量?
  • 如果您决定使用单例,也许您希望为某些有限的资源集合设置一些其他私有非单一资源管理器?然后考虑将singleton设计为包装模板类,以使这些代码成为可能:
    ResourceHandle h1 = Singleton<ResourceMan>::instance->getResource("foo");
    ResourceMan myPrivateManager;
    ResourceHandle h2 = myPrivateManager.getResource("bar");
Run Code Online (Sandbox Code Playgroud)
  • 您是使用一个综合管理器管理所有类型的资源还是为每种资源类型使用特殊管理器类?第二种方法更好.开发第二种方法的想法,为您编写代码,为您编写代码!使用模板资源管理器类和专用方法的小子集.只需为每种资源类型专门设计一种资源创建方法,并且不要触及所有其他资源管理代码!
  • 考虑资源生命周期.当一个特定的VAO应该被销毁?考虑实施参考计数器和/或借用参考.
  • 缓存?将主机内存加载到设备(显卡)后立即删除数据或保留一段时间?多长时间?
  • 流媒体怎么样?它不应该是资源管理器的域,但流支持可能会影响它.
  • glIsVertexArray函数及其类似物可能很有用.

排序

VAO不是渲染场景时需要更改的唯一资源.您还需要更改纹理,着色器甚至帧缓冲区减少状态更改次数的常用方法是按某些属性对可显示对象进行排序.

例如,您很可能只使用一个着色器渲染给定的网格物体.这就是为什么首先你可以通过着色器对所有网格进行排序,以便最小化着色器更改的数量.然后对于每个着色器(即在给定着色器的网格列表中),您可以按VAO对网格进行排序,以减少VAO更改的数量.最小.按纹理排序?如果您需要按纹理对对象进行排序以及在何处进行排序,这取决于您的应用程序.

结论

总而言之,如果您正在编写游戏引擎,那么无论如何您都需要资源管理器.如果你为VAO编写一个快速而又脏的部分解决方案,那么你将面临处理纹理,附加帧缓冲区和许多其他对象的完全相同的问题和问题,因此最好一次实现一个好的资源管理器.

有用的文章开头:

http://www.gamedev.net/page/resources/_/technical/game-programming/a-resource-manager-for-game-assets-r3807

http://www.gamedev.net/page/resources/_/technical/game-programming/a-simple-fast-resource-manager-using-c-and-stl-r2503

很有用的书:

http://www.gameenginebook.com/