转载请注明出处为KlayGE游戏引擎
上一篇解决了透明物体的渲染问题;本文将挑战另一个实时渲染的神话,实时全局光照(GI)。
实时全动态GI
目前direct lighting在游戏中日趋成熟,比较前卫的游戏引擎已经不满足于diect lighting的效果了,逐渐开始尝试indirect lighting。早期的方法有通过离线渲染light map来实现静态场景、静态光源的GI。接着出现了PRT,可以处理静态场景、动态光源。CE3用了Light Propagation Volumes的方法,不需要预计算,可以产生动态场景、动态光源的diffuse GI。不过其速度和质量确实不敢恭维。难道就不能有全动态场景、全动态光源、diffuse和specular通吃的实时GI方法吗?有!Multiresolution splatting for indirect illumination(MRSII)前来救驾。
在KlayGE 3.12中,团队成员atyuwen就已经实现了MRSII。经过半年多的改进,这种GI方法已经融入了新的Deferred Rendering框架中,并且性能也得到了很大的提升。下面就让我们来看看这种神奇的GI。
MRSII的渲染流程如下(感谢vanish整理了此流程图):
首先,G-Buffer需要做mipmap,接着在每一层检测深度和法线的间断点,把那些间断点在stencil buffer中标记出来,得到了这样的stencil buffer:
和之前的stencil规则一样,最高位是1表示忽略。所以灰色的pixel是可以忽略掉的,黑色的是需要计算光照的。可以看出黑色所占的面积并不大,绝大部分pixel都被略过了。
另外,还需要生成一个Reflective shadow map。和shadow map类似,RSM也是从光源视角渲染一遍场景。除了深度以外,RSM还需要保存normal和flux信息。把RSM采样出一些点,比如256个,作为 虚拟点光源(VPL)。目前KlayGE里面用的是均匀采样的方式,以后将改成importance sampling的方式提高VPL分布效率。
最后,每个VPL都可以根据BRDF生成一个light volume。用这些light volume去照亮G-Buffer的每一层。初始的light volume是个半球,在它的vertex阶段会根据各方向反射的亮度拉出某些顶点,生成一个奇怪形状的light volume。这个阶段因为涉及到大量的填充和计算,非常耗时,但因为stencil test是打开的,绝大部分pixel都会被挡掉,真正参与计算的pixel数远远少于G-Buffer的总pixel数,GI因此得到明显的加速。经过 测试,在目前的场景下,如果只用一层G-Buffer(也就是不用multiresolution),速度只有用三层的一半。如果大于三层,速度已经没有 提高了。所以默认就选了三层G-Buffer。
在生成每一层的indirect lighting结果之后,还需要做一个特殊的插值upsampling,才能得到光滑的结果。这个插值在MRSII的原paper中有描述,这里就不累赘了。
如果只是用一般的最近点插值或者双线插值,结果会有很多悲催的锯齿:
最后,把indirect lighting加到direct lighting中,继续做下一步的shading pass。最终结果如下:
比较只有direct lighting的结果,可以看到右边和地面被照亮了:
用了MRSII后,对于512×512的RSM、256个VPL、三层G-Buffer的情况下,GI在GTX480上只需要1.09ms、在 9800GT上需要4.3ms。目前还有不少性能空间可以挖掘,我预计在同质量的情况下,最终能达到在GTX480上0.5ms、9800GT上 2.5ms的速度。
这套GI的框架不但可以做这样的反射型indirect lighting,也可以做caustics这样的高频反光,也可以处理sub-surface scattering等材质效果。在KlayGE以后的版本中,MRSII将会得到持续的发展。
本篇详细讲解了实时GI的做法,下一篇是关于post process的改进。