« 最近玩的几个游戏 | 返回首页 | 随便写写 »

点光源的管理

我们 3d engine 的点光源相关的代码,以前设计的是比较糟糕的。最近几天,我决定自己动手重新设计和实现这块东西。性能倒是次要的东西,重要的是要把模块分离,减低耦合度。

这个光源模块设计要解决的问题在于:

GPU 的处理能力,目前看来比较有限,不可能实时处理无限数量的光源。所以,当你的场景里设置了许多的光源时,必须可以拣选出最可能影响被渲染物体的光源信息,把这个信息交给驱动去处理。

前任负责人在实现时,把光源揉杂在场景管理模块和渲染模块中去写,虽然功能实现了,但是维护麻烦。而且里面有很多需要性能优化的地方,由于结构设计不佳,导致动起来比较困难。更别说换个人去维护了。

增加关于光的新特性也会相对复杂。

经过一些思考后,我决定重新设计和实现。


虚拟场景是树状层次结构的,这个是很自然的设计。但是树状结构也比较难操作。而光源的管理,假设不处理复杂的遮挡(暂时我们游戏也没这个需求,有的话,也不违背以下设计),其实我们要的仅仅是知道所有激活光源在世界中的绝对位置。

所以,我把光源的位置管理藏在光的管理模块内部,在接口上并不暴露出这一信息。

作为光管理模块,我们需要的仅仅是把逐个光源设置进它自己内部维护的一个虚拟场景中(无层次结构)。然后在真正渲染时,取回空间中某个位置附近的光源而已。

为了给以后的优化留下足够的信息。我增加了一个受光体的概念。受光体保留自己在光场景中位置。我们向光的管理模块提供受光体对象,而不直接提交位置信息。这样可以留下 cache 层,加快查询速度。


这样,渲染流程就变成了:遍历场景树,将每个场景节点提交到下一层次。若是可渲染物,就提交到了渲染队列;若是光源,就把光源提交到了光管理模块。

在低一个层次,处理渲染队列时,经过若干排序和筛选操作后,真正渲染前,依据每个渲染物绑定的受光体对象,去光管理模块查询附近的光源,把这些光源包含的光信息提交给驱动层。


这里可以做的优化:

每个渲染帧,从接口上看,其实都从头构建了光源和受光体构成的场景(藏于光管理模块的内部,独立于场景树)。我们可以 cache 上一帧的场景,并加以比较。对于不变的对象,从受光体的内部取出以前计算好的值,直接返回。

光源位置管理,可以使用八叉树。如果是固定第三人称视角的游戏,四叉树也足够了。或者更简单一点,使用网格。设置光源坐标时,帧之间加上脏标记,可以轻易的获知是否需要重复运算。这个优化手段,可以把所谓静态光源和动态光源的处理细节隐藏在接口的背后。

打格子的方法可能是最简单有效的优化手段,比用其它复杂的算法计算一个受光体附近最近的若干光源更符合 KISS 原则。为了使格子简单有效,可以把格子的边长设置为略小于一个点光源大约可以影响的半径之内。每个点光源提交的时候,以围绕其坐标,以等边三角形的方位,提交三次即可。最终,只需要遍历受光体所在格子,就可以快速查到其附近的光源。

当然,一开始,我们不需要作任何优化手段。用最苯的 O(N*N) 的算法,检索所有提交的光源即可。先做对,再做好。

Comments

"从受光体的内部取出以前计算好的值,直接返回。",光照计算的结果是如何保存的?如果是用CPU来计算光照,光照的数值还可以保存,用GPU计算的话如何保存,保存到贴图中么?
re 2楼,crytek确实公布了渲染海量点光源的方案,可以看他们siggraph09的论文,虽然那东西不见得能在现在国内的游戏用上。。 当然,想采用新方案,无论是defered shading还是crytek的,依照你现在的方案,大修改是免不了的。要彻底解耦合渲染流程的变更,太难
用deffershading吧,场景管理会简单很多,光照都成后处理了
网易的3D引擎效果怎么样?有在做游戏了吗?
看不太懂,太深奥了。
SIGGRAPH 2009 CryENGINE 3的光照看起来很不错!
这个设计思路是正确的,scene graph的逻辑结构和用于渲染的加速结构要独立开来,加速结构可以在每次渲染的时候重建,宁可损失一些性能和内存,也不能将两者揉在一起,否则扩展性会很差。

Post a comment

非这个主题相关的留言请到:留言本