你所描述的被称为
资源管理器,或者至少是资源管理器的一部分。在外部文件中描述资源是一个好习惯,因此您需要一个资源文件,在其中以某种方式描述所有网格(考虑使用XML或JSON)。
类层次结构
以下是可能的类层次结构方法:
每个VAO代表一个网格,定义其顶点坐标、纹理坐标、法线、顶点颜色等。我认为除非您有一个非常特殊的可视化情况,否则没有理由在几个VAO中使用相同的VBO。因此,假设您仅使用每组数据一次,即使用VAO的类不应了解任何关于底层VBO的信息,并且不需要编写VBO的类包装器。
一组网格(可能只包含一个网格)表示一个模型。最小模型类应包括对VAO的句柄和几何变换信息(旋转、平移等)。为什么不是每个模型严格一个网格?有时您可能需要对一组网格应用一个变换,而其中每个网格都有自己的模型本地变换。例如,这种组合可以用于某种骨骼动画或仅用于渲染从可能武器库中取出的任意武器的角色。此外,您可以将这些模型分组在一起,获得具有相同接口的更复杂的模型,因此您将获得
场景图的相似之处。无论如何,将
组合模式用于模型类是一个好主意。
场景应包括模型集合、光源、力场等。
资源管理器
但是场景(或类似的游戏对象)从哪里获取它的模型呢?资源管理器应该回答这个问题。为每个模型定义一种独特的标识符。在最简单的情况下,真实或虚拟文件系统中的路径可以被视为标识符,但它并不是非常灵活。在我看来,最好使用具有表现力的人类可读名称在资源文件中定义所有网格,并将每个名称绑定到一组数据(所有类型的坐标、颜色等)和属性。
所有你的代码都不应直接使用模型,而应该使用资源管理器给你的句柄。显然,资源管理器必须在程序执行期间和不同地方的调用之间保持状态。它旨在跟踪哪些网格已经存储在内存中,并保留所有存储网格的VAO标识符。考虑使用
singleton模式作为资源管理器。
例子:
ModelHandle footman = resMan->getModel("footman.model");
footman->setLocation(x,y,z);
footman->draw();
这里调用getModel("footman.model")开始构建模型,导致像下面这样的调用:
MeshHandle resMan->getMesh("footman1.mesh");
获取所有网格。 getMesh
完成了这个解释的工作。它检查是否之前已经加载了这样的网格,如果是,则只返回包含此网格的VAO的句柄。否则,它将创建新的VAO对象,将请求的数据加载到其中,并返回对新创建对象的句柄。此后对该对象的所有后续请求都不会导致新的VAO分配。
当然,所描述的场景图组织只是大概近似于它应该看起来像什么。例如,它没有区分模型和抽象场景图节点,但为您的引擎开发和微调这种层次结构是由您决定的。
资源管理器类的最终接口是另一个需要讨论和设计的主题。一些问题和想法:
- 您将使用单例还是因为某些原因决定使用全局变量?
- 如果您决定使用单例,也许您希望拥有一些其他私有非单例资源管理器来处理一些有限的资源集合?那么请考虑将单例设计为包装模板类,以使得这样的代码成为可能:
ResourceHandle h1 = Singleton<ResourceMan>::instance->getResource("foo");
ResourceMan myPrivateManager;
ResourceHandle h2 = myPrivateManager.getResource("bar");
- 你会使用一个全面的管理器来管理所有类型的资源,还是为每种资源类型使用特殊的管理器类?第二种方法更好。发展第二种方法的想法,让编译器为您编写代码!使用带有少量专门方法的模板资源管理器类。只需为每种资源类型专门化一个资源创建方法,即可使所有其他资源管理代码不受影响!
- 考虑资源的生命周期。何时应该销毁特定的VAO?考虑实现引用计数和/或借用引用。
- 缓存?在将数据加载到设备(显卡)后立即从主机内存中删除它,还是保留一段时间?保存多长时间?
- 关于流媒体怎么样?这不应该是资源管理器的领域,但流媒体支持可能会影响它。
- glIsVertexArray函数及其类似函数可能会很有用。
排序
渲染场景时,VAO并不是您需要更改的唯一资源。您还需要更改纹理、着色器甚至是帧缓冲区等。减少状态更改数量的常见方法是按某些属性对可显示对象进行排序。
例如,很可能你只使用一个着色器来渲染给定的网格。因此,你可以首先通过着色器对所有网格进行排序,以最小化着色器更改的数量。然后,对于每个着色器(即在给定着色器的网格列表中),你可以按VAO对网格进行排序,以将VAO更改的数量降至最低。按纹理排序?这取决于你的应用程序是否需要按纹理对对象进行排序以及在哪里进行排序。
结论:
总之,如果你正在编写游戏引擎,那么你肯定会需要资源管理器。如果你为VAOs编写了一个快速且不完整的解决方案,那么你将面临处理纹理、附加帧缓冲区和许多其他对象的同样问题和疑问,因此最好实现一个良好的资源管理器。
有用的起步文章:
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/