转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=3370

KlayGE里很早就支持屏幕空间实时非平面反射,并在后来扩展到了全方向的反射。虽然比传统的反射能少渲染一遍场景,速度有明显提高,但由于计算完全在像素级,开销仍然比较大。本篇将探讨一下如何加速反射的渲染,主要思路来自于SIGGRAPH 2014 Advances in Real-Time Rendering in Games里的Reflection System in Thief

原始效果

拿Ocean例子来统计速度。在NVIDIA Geforece GTX 960上,没有反射的时候249FPS,有反射的时候就剩下159FPS了。也就是说,反射占了2.27ms左右。

Ocean Reflection Full

加速1:半分辨率

既然是PS的瓶颈,那么最直接的优化方法就是降低分辨率。

原先的反射是在special shading里面计算的,必须是全分辨率。在新的改进里,renderable里增加了reflection pass,反射被提到了一个单独的pass。注意这里可以是SSR也可以是其他形式的反射。这个pass的目标是个半分辨率的纹理,称为reflection map。在special shading里,只要根据屏幕空间的位置读个颜色即可。这个修改非常简单,只要把计算反射的部分单独拆出去就行了。

经过这个优化,速度提升到了194FPS,反射部分只要1.14ms,提速了50%。但是,渲染效果有了明显下降,肉眼可见。

Ocean Reflection Half

加速2:提高访存一致性

反射的速度除了分辨率,还有个一致性的问题。相邻像素之间由于normal差别很大,反射射线也会相交到很不相同的地方。GPU最怕这样的情况了,一遇到只能按照最保守分支最多的一个像素来计算。也就是说一个慢的像素就会影响一个局部的速度。

Reflection System in Thief里用到的技巧是,把normal都取为(0, 1, 0),也就是向上。这样反射射线交到的像素大部分都是连续的,性能也会因此提升很多。但是,这么做的话就是“静如止水”的效果,没有波浪了。这里的另一个技巧是,在取reflection map的时候根据真实的normal偏移一下采样纹理坐标。这样做恢复了波浪的效果,可以消除半分辨率带来的粗糙纹理,并且可以根据偏移的强度调反射图像的效果。实际上现实中的水面反射因为画面破碎,并没有那么好看,比不上hack的方式。

经过这个优化,速度提升到了230FPS,反射部分剩下0.33ms,提速85.4%。速度明显提高,并让效果变成美术可调的方式。

Ocean Reflection Half Uniformed

加速3:shader优化

Reflection System in Thief还提到了一些优化,比如在shader里加上early out。在发现肯定不会相交的时候提前return。另一个优化是,反射的采样数根据像素到视点的距离,按exp分布来减少。目前KlayGE里是线性减少的,还不是exp。以后会进一步尝试这些优化,继续提高性能。

总结

develop分支里,反射的速度前后提高了85%。反射的打开与否并不特别影响性能了。SSR更加实用了。