滚动我自己的场景图


23

您好游戏开发SE!

我正在通过OpenGL爬行,希望创建一个简单且非常轻便的游戏引擎。我认为该项目是一种学习经验,最终可能会赚点钱,但无论哪种方式都会很有趣。

到目前为止,我已经使用GLFW获得了一些基本的I / O,一个窗口(带有一个非常漂亮的F11全屏键)以及一个OpenGL上下文。我还使用GLEW公开了其余的OpenGL扩展,因为我正在使用Windows,并且我想使用所有OpenGL 3.0+。

这使我进入了场景图。简而言之,我想自己动手。这个决定是在看完OSG并阅读了几篇有关场景图的概念如何扭曲,弯曲和折断之后做出的。一篇这样的文章描述了场景图如何发展为...

然后,我们添加了所有这些额外的东西,例如将装饰品挂在圣诞树上,除了一些装饰品是多汁的牛排,还有一些是整头活牛。

打个比方,我想要牛排,场景图应该是什么样的肉,而不必捆成一堆多余的代码或任何整头母牛。

因此,考虑到这一点,我发现自己确实想知道场景图应该是什么以及应该如何实现简单的场景图?到目前为止,这就是我所拥有的...

一棵父树,n子树或DAG,其中...

  • 应该跟踪游戏对象的转换(位置,旋转,比例)
  • 应该保留渲染状态以进行优化
  • 应该提供剔除视锥范围内的对象的方法

具有以下属性...

  • 所有节点都应视为可渲染的(即使它们不渲染),这意味着它们...

    • 应该都具有cull(),state()和draw()方法(如果不可见,则返回0)
    • cull()递归地在所有子节点上调用cull(),从而为整个节点和所有子节点生成一个完整的cull网格。另一种方法hasChanged()可以使所谓的静态网格物体不必在每帧中都计算出其剔除几何形状。这将起作用,如果子树中的任何节点发生了更改,则所有几何结构都将重建到根。
  • 渲染状态将保存在一个简单的枚举中,每个节点将从该枚举中选择所需的OpenGL状态集,并且将在该节点上调用draw()之前设置该状态。这样可以进行批处理,将将给定状态集的所有节点一起呈现,然后设置下一个状态集,依此类推。

  • 任何节点都不应直接保存几何/着色器/纹理数据,而应指向共享对象(可能由诸如资源管理器之类的单例对象管理)。

  • 场景图应该能够引用其他场景图(也许使用代理节点),以允许像的情况下,因此允许复杂的多网眼模型/对象周围的场景图被复制而不增加一吨的数据。

我希望获得有关当前设计的一些有价值的反馈。是否缺少功能?有没有更好的方法/设计模式?我是否错过了一些更简单的3D游戏必须包含在此设计中的更大概念?等等。

谢谢,-Cody

Answers:


15

这个概念

从根本上说,场景图无非是双向无环图,它代表了层次结构的空间关系集。

如前所述,在野外的引擎倾向于在场景图中包含其他优点。您是将其视为肉还是牛,可能取决于您使用引擎和库的经验。

保持轻巧

我更喜欢Unity3D风格,即场景图节点(本质上是拓扑,而不是空间/地形结构)本身包含空间参数和功能。在我的引擎中,我的节点甚至比Unity3D更轻巧,它们从超类/已实现的接口继承了很多不必要的垃圾成员:这就是我所拥有的-尽可能地轻巧:

  • 父/子指针成员。
  • 转换前的空间参数成员:xyz位置,俯仰,偏航和横滚。
  • 变换矩阵;层次结构链中的矩阵可以通过在树上递归地上下移动而快速,轻松地进行乘法运算,从而为您提供了层次结构的空间变换,这是场景图的主要特征;
  • 一个updateLocal(),其更新方法仅此节点的变换矩阵
  • 一种updateAll()更新方法以及所有后代节点的变换矩阵的方法

...我的节点类中还包括运动方程逻辑,因此还包括速度/加速度成员(线性和角度)。您可以放弃它,而可以根据需要在主控制器中处理它。就是这样-确实非常轻巧。记住,您可以在数千个实体上使用它们。因此,正如您所建议的,请保持清淡。

构建层次结构

您对引用其他场景图的场景图说的是什么...我正在等待紧急情况?当然可以。那是他们的主要用途。您可以将任何节点添加到任何其他节点,并且转换应自动在新转换的本地空间内进行。您要做的只是更改一个指针,这并不像您要复制数据!通过更改指针,您将获得更深的场景图。如果使用Proxies可以使事情更加高效,那么我从来没有见过这种需要。

避免与渲染相关的逻辑

编写场景图节点类时,无需再进行渲染,否则您将自己感到困惑。重要的是,您拥有一个数据模型-不管是场景图还是无关紧要-且某个渲染器将检查该数据模型并相应地渲染世界中的对象,无论是在1、2中,3或7个尺寸。我要说的是:不要用渲染逻辑污染场景图。场景图是关于拓扑和地形的-即连通性和空间特征。这些是模拟的真实状态,甚至在没有渲染的情况下也存在(在阳光下,从第一人称视角到统计图再到文字描述,都可以采取任何形式)。节点不指向与渲染相关的对象,但是相反的情况可能是正确的。另外考虑一下:并非整个树中的每个场景图节点都是可渲染的。许多将仅仅是容器。那么,为什么还要为指向渲染对象的指针分配内存呢?即使是从未使用过的指针成员,仍然会占用内存。因此,反转指针方向:与渲染相关的实例引用数据模型(可能是或包括场景图节点),而不是相反。并且,如果您想通过一种简单的方法来遍历控制器列表,但仍可以访问相关视图,则可以使用字典/哈希表,其访问时间接近O(1)。这样就不会造成污染,而且您的仿真逻辑也不会在意什么渲染器,这使您日以继夜地进行编码 那么,为什么还要为指向渲染对象的指针分配内存呢?即使是从未使用过的指针成员,仍然会占用内存。因此,反转指针方向:与渲染相关的实例引用数据模型(可能是或包括场景图节点),而不是相反。并且,如果您想通过一种简单的方法来遍历控制器列表,但仍可以访问相关视图,则可以使用字典/哈希表,其访问时间接近O(1)。这样就不会造成污染,而且您的仿真逻辑也不会在意什么渲染器,这使您日以继夜地进行编码 那么,为什么还要为指向渲染对象的指针分配内存呢?即使是从未使用过的指针成员,仍然会占用内存。因此,反转指针方向:与渲染相关的实例引用数据模型(可能是或包括场景图节点),而不是相反。并且,如果您想通过一种简单的方法来遍历控制器列表,但仍可以访问相关视图,则可以使用字典/哈希表,其访问时间接近O(1)。这样就不会造成污染,而且您的仿真逻辑也不会在意什么渲染器,这使您日以继夜地进行编码 并且,如果您想通过一种简单的方法来遍历控制器列表,但仍可以访问相关视图,则可以使用字典/哈希表,其访问时间接近O(1)。这样就不会造成污染,而且您的仿真逻辑也不会在意什么渲染器,这使您日以继夜地进行编码 并且,如果您想通过一种简单的方法来遍历控制器列表,但仍可以访问相关视图,则可以使用字典/哈希表,其访问时间接近O(1)。这样就不会造成污染,而且您的仿真逻辑也不会在意什么渲染器,这使您日以继夜地进行编码世界更容易。

至于淘汰,请参考上面的内容。感兴趣区域剔除是一种模拟逻辑概念。也就是说,您不会处理此(通常是盒装,圆形或球形)区域之外的世界。这发生在渲染发生之前的主控制器/游戏循环中。另一方面,平截头体剔除完全与渲染相关。因此,现在就不必选择淘汰了。它与场景图无关,通过专注于场景图,您将掩盖您想要实现的目标的真实目的。

最后说明...

考虑到此处包含的所有渲染细节,我强烈感觉到您来自Flash(尤其是AS3)背景。是的,Flash Stage / DisplayObject范例包括所有渲染逻辑作为场景图的一部分。但是Flash做出了很多不必要的假设。对于成熟的游戏引擎,出于性能,方便和通过适当的SoC控制代码复杂性的原因,最好不要将两者混为一谈


1
谢谢尼克。我实际上是一名3D动画师(真正的3D而不是Flash)转为程序员,因此我倾向于考虑图形方面的问题。如果那还不够糟糕,那我就是从Java开始的,并且一直以这种语言灌输的“一切都必须是一个对象”的心态来窥探自己。您已经说服了我,场景图应该与渲染和剔除代码分开,现在我的工作正紧紧围绕应该如何实现。我想处理喜欢渲染自己的独特系统的引用场景图的转换数据,等等
科迪·史密斯

1
@CodySmith,很高兴提供了帮助。无耻的插头,但我保持一个框架,这是所有有关的SoC / MVC。在这样做时,我受到了业内更传统的阵营的吹捧,他们坚持认为所有事物都应集中在一个整体的整体对象中。但是即使他们通常会告诉您-将渲染与场景图分开。SoC / SRP是我无法充分强调的东西-切勿将超出您所需的更多逻辑混合到一个类中。我什至会提倡在同一个类中使用复杂的OO继承链,而不是混合逻辑,如果您对我持枪的话!
工程师

不,我喜欢这个概念。没错,这是我多年来阅读有关游戏设计的文章中第一次看到的SoC。再次感谢。
科迪·史密斯

@CodySmith再次浏览时快速想到。总的来说,保持事物的分离是一件好事。对于不同类型的代码库模型的控制器对象经历渲染的,但是,它对你罚款,以保持集合RenderableS(这是一个接口或抽象类)内部,以这些核心模型的控制器对象。实体或UI元素就是很好的例子。因此,您只能快速访问与该特定核心对象相关的那些渲染器,而无需实现会污染实体类并因此使用接口的具体实现。
工程师

@CodySmith实体的好处显而易见,例如。在世界视口和小地图上都有制图表达。因此,收集。或者,您可以为每个模型控制器对象在其内部只允许一个渲染器插槽。但是保持界面通用!没有细节-只是Renderer
工程师
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.