Unity室内场景+光照练习 2

作者:在木
2017-07-13
10 8 1

前言

在准备这篇文章的时候,无意之中发现了一个讲不同渲染通道(Rendering Path)的文章,讲了Forward,Deferred 和 Forward+ 三种渲染的流程,而且还附了特别详尽的代码,于是就浪费掉了两个晚上去学习/(ㄒoㄒ)/~~。

千万别错过,要是我当年看过这篇文章说不定就可以把大神们秒了,趁热打铁,那我们就先上算法吧。

渲染通道(Rendering Path)

(这部分的图片均来自以下网站)

Forward+ Rendering

向前(Forward): 简单粗暴的蛮力算法。对于屏幕上的每个像素,映射到对应物体上,得到投影点,材质,法线以及一系列的数据。然后遍历灯光,计算所有灯光对于该点的作用的光照的数据(简单来说就是一个颜色值),然后进行线性叠加,最后得到该像素点颜色。当然真实的计算中并不是这么简单,还需要考虑反射光等等情况,这里需要用到光线跟踪的技巧,就不展开了。

延迟(Deferred): 这个算法核心的思想就是对场景进行预处理。把每个像素点对应的实体物体(opaque objects)上的深度(depth),法线(normal),颜色(diffuse),亮度(specular)和发光环境光(Light Accumulation)等等系数计算好,存放在若干张缓存图中(这里使用的是经典的漫反射模型),由于缓存图的寄存器等级比较高,那么就把多次读取物体信息的时间给省下来了。还有一个简化思想是,灯光是有影响范围的,把超过影响范围的空间(光强度过小)内的物体剔除掉。比如点光源的影响空间就可以视为一个球,球外面的物体视为不被该光源影响。

值得一提的是在对透明物体(Transparent Objects)的渲染中,该方法退回到向前渲染。

参考网站:Learn OpenGL, extensive tutorial resource for learning Modern OpenGL

向前加(Forward+): 对向前算法的优化。这个算法的基本思想就是给像素点分组,给每组像素进行统一的光线剔除,这样就把在一个组内的像素判断是否被某光源影响的时间给省掉了,并且可以进行并行计算。这个算法首先把屏幕上的像素点切分成一些列的网格(grid)。

然后对于每一个大块的网格,分透明和不透明物体进行渲染深度的计算,也就是在这个网格对应的网格视锥(Grid Frustum,也就是这个网格的面和视点连起来然后反向无线延伸形成的一个无限延伸的棱锥)里,需要渲染物体的所有物体的最近和最远的深度(也就是眼睛在这个网格上能看到的物体的最近和最远的距离)。

参考延迟渲染的部分,每个光源都是有一个作用范围的。对于每个网格,遍历光源,若是某个光源作用范围进入到了该网格的渲染范围(上图灰色的部分),则该光源被列入该网格的光源列表里。最后对于每个网格内的像素,只用该网格的光源列表进行向前渲染的计算。

(在文章中还有一些存储格式,空间几何算法的说明,这里就不再赘述了)

复杂度

最后考查一下各个算法的复杂度,光源数为L,物体数为O,屏幕像素数为P×P。

Forward:LOPP

Deferred:(L+O)PP

Forward+:(最坏)LOPP (最好) L+O

Forward+其实是一个并不是很稳定的算法,但越是复杂的场景,小灯光越多越能够体现Forward+的优势。另外由于算法的原理,Forward+实际上对于方向光源是没有优化的。

以下是实测数据:

对于大灯光为主的场景,延迟(Deferred) 和 向前加(Forward+)的效率相当,比向前(Forward)效果明显的更好。

对于小光源多的场景,向前加(Forward+)的效果明显更好,注意上图的横坐标的数字是指数增加的,用Forward +可以把灯光堆到5000个,我的天!

PS : 根据 Charlie 的评论,这里再给出一个Deferred Rendering的优化:TBDR

这个博客好像写得也比较详细了,我就不再赘述,直接上链接:

Jim's GameDev Blog

光照烘焙

把场景中各个物体设置为静态(static),至少是光照贴图静态(light map static)。

2_compressed

设置场景的光源属性为烘焙。这里以场景中的方向光源(directional light)为例。设置如下:

3_compressed

Mode :渲染模式。可选的模式为Realtime, Baked, Mixed. Realtime即为实时渲染,Baked为影响烘焙,Mixed为同时影响烘焙和实时。

Indirect Multiplier:就是前几个版本里的 bounce intense。在渲染的时候,物体会接收到一些不是直接从光源发射出来的光线,比如从其他物体反射过来的光线。这些光线叫做Indirect light。对这种光线的计算会在一定程度上的影响效率。这里可以把Indirect Multiplier设为0避免这种反射光的计算,然后用其他技术来补充环境反射光。

Shadow Type:有No shadow, Hard Shadow 和 Soft Shadow三个选项。我们这里选择Soft Shadow, 并且把下面的Baked Shadow Angle 设为15。由于实际生活中的阳光/灯光并不是方向光/点光源,而是某种体积光,所以会产生软阴影,原理大概如下:

4_compressed

图片出处

投影的计算方法比较有名的有Shadow volume和Shadow mapping。提供一个解释Shadow mapping的传送门:

Learn OpenGL, extensive tutorial resource for learning Modern OpenGL

然后打开光照设置窗口,Windows=>Lighting=>Settings

5_compressed

在弹出窗口的Scene框下,设置如上图。

Mixed Lighting - Lighting Mode:这个选项共有四个,分别对应了场景在多大的程度上使用Light Map。比如Backed Indirect表示所有的静态和动态物体都用实时光,Subtractive表示静态和动态物体都用烘焙光(表达不准确,但是大概是这个意思)。下面的链接提供了关于这四个选项的详细说明。

Lighting Modes in 5.6 Beta 2

Lightmap Resolution:这个选项决定了光照烘焙的清晰度,要是觉得烘焙光照锯齿严重,可以调高这个参数。

Ambient Occlusion:环境遮挡,这个技术的概念就是,物体的表面会由于其物理上的凹凸导致接收到光线强度的变化。原理如下图所示,在P点接收到的光线由于附近物理表面的遮挡而减少了。

6_compressed

其效果嘛,简单来说,就是给物体加上一层阴影,使其棱角更加清晰,明暗交界的部分更加的明显。AO一般分为Object Space和Screen Space两种实现。Object Space 有通过材质贴图的(比如上一节讲的PBR中的AO贴图),或者光照贴图。这里计算的就是Object Space Ambient Occlusion中光照贴图的部分。

其下的参数,distance表示了检测的范围。Indirect Contribution和Direct Contribution分别表示环境光和光源的影响系数。

Final Gather这个选项需要打开,并且把Ray Count调高到一个合适的数值。Final Gather 是一个计算非直接光源(也就是环境光,indirect light)的一个技巧。它的主要原理是在受到光照的点向不同角度发射一定数量的射线,计算射线方向的光强度然后求平均。其直观效果是能够增加光照贴图的分辨率。这里找到一篇在Maya里的FG分析的文章,勉强凑合一下吧:

Global Illumination in Maya 8 - Indoor

这里的Ray Count就是指射线的数量了。

点击最下面的按钮Generate Lighting。等待数分钟后场景的光照贴图便生成好了。

最终效果如下:

title