背景:
我正在为使用C ++和OpenGL的实体组件系统类型体系结构设计一个简单的3D渲染系统。该系统由一个渲染器和一个场景图组成。完成渲染器的第一次迭代后,我可能会将场景图分发到ECS架构中。目前,它是一种占位符。如果可能,以下是我对渲染器的目标:
- 简单性。这是一个研究项目,我希望能够轻松更改和扩展我的系统(因此采用ECS方法)。
- 表现。我的场景可能有很多小型模型,也有很多具有很多几何图形的体积。从OGL上下文中获取对象并在每个渲染帧中缓冲几何体是不可接受的。我的目标是数据本地化,以避免缓存未命中。
- 灵活性强。它必须能够渲染精灵,模型和体积(体素)。
- 解耦。编写渲染器后,场景图可以重构为核心ECS体系结构。
- 模块化。能够在不更改场景图的情况下交换不同的渲染器将是很好的。
- 参照透明性,表示在任何时间点我都可以给它任何有效的场景,并且它将始终为该场景渲染相同的图像。特别地,该目标不是必需的。我认为这将有助于简化场景序列化(我将需要能够保存和加载场景),并为我提供了在运行时出于测试/实验目的而在不同场景中交换的灵活性。
问题与思路:
我想出了一些不同的方法来尝试,但是我正在努力为每个渲染节点缓存OGL资源(VAO,VBO,着色器等)。以下是到目前为止我已经想到的不同的缓存概念:
- 集中式缓存。每个场景节点都有一个ID,渲染器具有将ID映射到渲染节点的缓存。每个渲染节点包含与几何图形关联的VAO和VBO。高速缓存未命中获取资源并将几何图形映射到高速缓存中的渲染节点。更改几何形状后,将设置脏标志。如果渲染器在遍历场景节点时看到肮脏的几何标志,则它将使用渲染节点重新缓冲数据。移除场景节点后,会广播事件,并且渲染器会在释放资源的同时从缓存中删除关联的渲染节点。或者,将节点标记为要删除,而渲染器负责将其删除。我认为这种方法最接近目标6,同时也考虑了4和5。2遭受了额外的复杂性和丢失了使用地图查找而不是数组访问的数据局部性的麻烦。
- 分布式缓存。除了每个场景节点都有一个渲染节点外,其他与上面类似。这会绕过地图查找。为了解决数据局部性,可以将渲染节点存储在渲染器中。然后场景节点可以改为具有指向渲染节点的指针,并且渲染器将指针设置在高速缓存未命中。我认为这种模仿实体组件方法,因此与架构的其余部分保持一致。这里的问题是,现在场景节点保存渲染器实现特定的数据。如果更改了事物在渲染器中的渲染方式(如渲染精灵与体积),则现在需要更改渲染节点或向场景节点添加更多“组件”(这也意味着更改场景图)。从好的方面来说,这似乎是启动并运行我的第一迭代渲染器的最简单方法。
- 分布式元数据。渲染器缓存元数据组件存储在每个场景节点中。此数据不是特定于实现的,而是包含ID,类型以及缓存所需的任何其他相关数据。然后,可以使用ID直接在数组中完成缓存查找,并且该类型可以指示要使用的呈现方法的类型(如精灵与体积)。
- 访客+分布式映射。渲染器是一个访客,场景节点是访客模式中的元素。每个场景节点都拥有一个只有渲染器才能操作的缓存键(就像元数据一样,只是一个ID)。该ID可以用于数组,而不是广义的地图查找。渲染器可以允许场景节点根据场景节点的类型调度不同的渲染功能,并且ID可以由任何缓存使用。默认或超出范围的ID将指示高速缓存未命中。
您将如何解决这个问题?或您有什么建议吗?感谢您阅读我的文字墙!
1
你有进步吗?
—
Andreas
这是一个非常复杂的问题,应该分成几个单独的问题。这实际上是在问“我应该如何设计我的引擎?” 我的建议是先设计一些支持更简单组件的东西,然后再添加和重构功能。另外,请查阅3D游戏引擎设计书籍,其中应涵盖您正在寻找的许多信息。
—
伊恩·杨