聚合国内IT技术精华文章,分享IT技术精华,帮助IT从业人士成长

层次结构和状态继承

2020-06-05 13:30 浏览: 589 次 我要评论(0 条) 字号:

在 blog 上,我写过好几篇关于场景管理模块的树结构的文章。这些也是我这两年在做游戏引擎中对象管理的思考历程。

通常游戏引擎中会把可渲染对象以树结构储存,这是场景管理模块最常见的作法。顺便说一句,GUI 界面也是用类似的方式。但是,我始终认为,从 gameplay 的层面上来看,游戏逻辑需要关注的对象并不需要用层次结构的方式管理。因为,空间结构上的层次很可能发生变化,从而引起关注的对象的层次路径变化。我们最终关注的那些东西不变,但它们在空间中的位置却会经常改变。

我一直在思考的问题是:为什么一定要用树结构组织可渲染对象?树结构到底带来了什么好处?

最直接的好处是,减少矩阵运算的次数。因为,渲染层最终需要对象在整个世界中的位置,而每个被渲染的部件本身却是逐级组合起来的(为了减少数据重复,我们不能因为一个部件换了个位置,就复制一次),部件只会记录相对整体的一个局部空间变换。如果我们平坦的保存没有可渲染部件,势必在计算它最终被渲染到屏幕时的世界矩阵的时候,需要连乘一长串局部矩阵。而组织成树结构,以一定的次序计算,可以大大减少最终矩阵乘法的数量。

但这一点好处,我认为还没有触及本质。表达空间位置的矩阵,仅仅是可渲染对象的一个属性而已。

层次结构的本质是让属性可以用继承的方式优化储存,并方便批量修改。对于每种属性,会定义一种对应的继承方法。

对于空间矩阵,如果一个对象没有自己的局部矩阵,那么它就继承了父亲的矩阵,如果有,继承的方式就是做 一次矩阵的乘法。修改根节点的空间矩阵,等价于修改了连同它的所有子孙的在空间中的位置。

其实,还有很多属性也需要继承。继承可以避免把相同的属性值复制到相关节点上,也方便了一组对象一起修改。

例如材质,当我们由于种种原因,将一个网格拆分成多个时(可能是因为网格顶点数量过多,也可能是因为它们的贴图不同),多个子网格的材质其实是基本一致的。

还有可使距离,用来控制摄象机距离多远的时候,就不再渲染该组对象。同一个物件,放在室内的时候可视距离比较近,放在室外的时候可视距离较远。

还有一类属性,可能简单到只是一个布尔量,但需要方便的成组改变。我能举出的例子有很多,下面只列出常见的几个:

  • 需见标记:我们有时需要临时关闭一组对象的显示,但又不希望删掉这些对象。
  • 投射阴影:让标记了的对象投射在阴影图上。
  • 接收阴影:在渲染的时候,考虑阴影图的影响。
  • 点选标记:被聚焦的时候,采用一种特殊的着色器高亮显示。
  • 水中倒影:需要绘制在水面的倒影中。

我觉得在实现的时候可以合并这类布尔量的标记,一起处理比较好。

用一个 64bit 整数保存 32 组状态。高 32bit 表示该状态是自己设置(1) 还是从父亲继承下来(0) 。低 32bit 表示每个状态(0/1) 。

这样,读取第 n 个状态可以简单的用 (state >> n) & 1

当我们需要从父亲继承状态时,可以使用:

local mask = state >> 32
state = ( parent.state & (mask~0xffffffff) | (state & mask) | (mask << 32)


网友评论已有0条评论, 我也要评论

发表评论

*

* (保密)

Ctrl+Enter 快捷回复