点光源的管理
我们 3d engine 的点光源相关的代码,以前设计的是比较糟糕的。最近几天,我决定自己动手重新设计和实现这块东西。性能倒是次要的东西,重要的是要把模块分离,减低耦合度。
这个光源模块设计要解决的问题在于:
GPU 的处理能力,目前看来比较有限,不可能实时处理无限数量的光源。所以,当你的场景里设置了许多的光源时,必须可以拣选出最可能影响被渲染物体的光源信息,把这个信息交给驱动去处理。
前任负责人在实现时,把光源揉杂在场景管理模块和渲染模块中去写,虽然功能实现了,但是维护麻烦。而且里面有很多需要性能优化的地方,由于结构设计不佳,导致动起来比较困难。更别说换个人去维护了。
增加关于光的新特性也会相对复杂。
经过一些思考后,我决定重新设计和实现。
虚拟场景是树状层次结构的,这个是很自然的设计。但是树状结构也比较难操作。而光源的管理,假设不处理复杂的遮挡(暂时我们游戏也没这个需求,有的话,也不违背以下设计),其实我们要的仅仅是知道所有激活光源在世界中的绝对位置。
所以,我把光源的位置管理藏在光的管理模块内部,在接口上并不暴露出这一信息。
作为光管理模块,我们需要的仅仅是把逐个光源设置进它自己内部维护的一个虚拟场景中(无层次结构)。然后在真正渲染时,取回空间中某个位置附近的光源而已。
为了给以后的优化留下足够的信息。我增加了一个受光体的概念。受光体保留自己在光场景中位置。我们向光的管理模块提供受光体对象,而不直接提交位置信息。这样可以留下 cache 层,加快查询速度。
这样,渲染流程就变成了:遍历场景树,将每个场景节点提交到下一层次。若是可渲染物,就提交到了渲染队列;若是光源,就把光源提交到了光管理模块。
在低一个层次,处理渲染队列时,经过若干排序和筛选操作后,真正渲染前,依据每个渲染物绑定的受光体对象,去光管理模块查询附近的光源,把这些光源包含的光信息提交给驱动层。
这里可以做的优化:
每个渲染帧,从接口上看,其实都从头构建了光源和受光体构成的场景(藏于光管理模块的内部,独立于场景树)。我们可以 cache 上一帧的场景,并加以比较。对于不变的对象,从受光体的内部取出以前计算好的值,直接返回。
光源位置管理,可以使用八叉树。如果是固定第三人称视角的游戏,四叉树也足够了。或者更简单一点,使用网格。设置光源坐标时,帧之间加上脏标记,可以轻易的获知是否需要重复运算。这个优化手段,可以把所谓静态光源和动态光源的处理细节隐藏在接口的背后。
打格子的方法可能是最简单有效的优化手段,比用其它复杂的算法计算一个受光体附近最近的若干光源更符合 KISS 原则。为了使格子简单有效,可以把格子的边长设置为略小于一个点光源大约可以影响的半径之内。每个点光源提交的时候,以围绕其坐标,以等边三角形的方位,提交三次即可。最终,只需要遍历受光体所在格子,就可以快速查到其附近的光源。
当然,一开始,我们不需要作任何优化手段。用最苯的 O(N*N) 的算法,检索所有提交的光源即可。先做对,再做好。
Comments
Posted by: baixiaosheng | (7) January 2, 2012 02:55 PM
Posted by: zwywilliam | (6) January 9, 2010 08:23 PM
Posted by: fancy | (5) January 1, 2010 12:48 PM
Posted by: Lin | (4) January 1, 2010 11:50 AM
Posted by: StarShine | (3) December 31, 2009 01:13 PM
Posted by: wen | (2) December 30, 2009 03:25 AM
Posted by: analyst | (1) December 29, 2009 11:24 PM