Unity 核心系统详解 -- 光照系统

1,409 阅读10分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Unity 引擎中提供的光照系统叫 EnLighten,作为引擎渲染的一部分,负责构建场景中的灯光。Unity 的灯光组件大致分为:光源组件和烘焙组件。

1 常见光源类型

1.1 Directional Light

平行光,几乎每个场景中都会使用到的光源对象,常用于模仿太阳光的效果。它与 Point Light 和 Spot Light 最大的不同在于,Directional Light 没有真正的“源”,整个场景中任何一个角落的光照强度是相等的。

在 Unity 中新建一个场景,鼠标右键->Create->Scene,此时可看到场景中默认有一个 Directional Light 和一个 Main Camera,各个光源对象在 Scene 视图中的图标不同,如 Directional Light 的图标是一个太阳。

事实上,Directional Light 的光照效果完全不受位置的影响,最直观影响 Directional Light 效果的是角度。如下图所示,在场景中调整 Directional Light 的角度为朝正上方,很明显可以看到整个场景变成了黑色,不再有光照的效果。

1.2 Point Ligth

点光源,从中心呈球形向四周扩散,例如火把,室内灯具。Point Light 的效果受到范围(Range)和强度(Intensity)的影响。和 Directional Light 不同,点光源的效果受到位置的影响。

在 Hierarchy 视窗中鼠标右键->Light->Point Light,从而在场景视图中添加了一个 Point Light 组件,接着使用移动工具将其移动到场景中间位置。然后再场景中添加几个 Cube,Sphere 等物体。

选中 Point Light 对象,然后在 Inspector 视窗中调整 Point Light 下的 Color 属性,将 Color 设置为更显眼的颜色,比如金黄色。

在 Inspector 视窗中调整 Range(范围)的值改变光照范围,当 Range 值越来越大时,场景中心较明显的红色区域也会越来越大。这是因为 Point Light 的范围特性--并不是范围内所有区域的灯光强度都是相同的,而是呈从中心向边缘递减。此外还可以调整 Intensity(强度)等参数。Point Light 同样支持 Hard Shadow 和 Soft Shadow。

1.3 Spot Light

Spot Light 从中心呈扇形向某一个方向发出,受扇形角度(Angle)和范围(Range)的影响。一般用于模拟手电筒和车灯等的光照效果。

在 Hierarchy 视窗中鼠标右键->Light->Spot Light,从而在场景视图中添加了一个 Spot Light 组件。Range 代表范围,Spot Angle 代表角度。

1.4 Area Light

区域光,与上面 3 个最大的不同在于只能在烘焙的情况下使用。而 Directional Light,Point Lignt,Spot Light 能在实时(RealTime)和烘焙(Bake)两种情况下使用。实时和烘焙的具体含义在后续章节详细说明。Area Light 用于一些较为特殊的场景,如在室内,以上 3 种灯光都无法较好地实现光照效果,此时我们就可以使用 Area Light 实现。

2 全局光照

前文只是介绍了各个灯光单独作用的场景。在实际开发中,大多数情况下灯光与物质是相互作用的,如灯光照射到物体 A 上,物体 A 反射的光会照射到物体 B。这种关联关系是通过全局光照(Global Illumination,GI)系统处理的。

下图显示了全局光照的作用效果,在一个封闭空间中两个玻璃球体互相反射,全局光照极大地提升了场景中光照的真实性,但这种实时计算是非常消耗资源的。

但是从另一个方面来说,只需要对场景中的动态物体实时计算来保证光照效果,但不应该在那些固定的物体上浪费太多资源。如果一个场景中所有物体全部是静止状态,那么实时计算光照效果显然是浪费资源的,我们只需要执行一次计算即可。

在 Unity 中,这种技术成为烘焙(Bake)。当对场景进行灯光烘焙后,场景中的光照信息就会存储在 Lightmap 中。当场景运行时,Unity 直接读取 Lightmap 中的数据,无须再进行实时计算,这样就很好地避免了不必要的资源浪费。

2.1 烘焙

在进行灯光烘焙前,首先需要告诉系统哪些物体需要被烘焙,哪些灯光用于烘焙。为了方便操作,首先对 Hierarchy 视窗中的游戏对象进行简单的整理。

步骤 1:在 Hierarchy 视窗中空白处,鼠标右键->Create Empty,将新创建的空白游戏对象命名为“Objects”,然后把方块、球体等拖到 Objects 下成为其子物体。接下来使用类似方式创建一个新的空白游戏对象并将其命名为 Lights,然后把刚才的 Directional Light、Spot Light 和 Point Light 都拖为 Lights 的子物体。

步骤 2:在 Hierarchy 视窗中选中 Objects,然后在 Inspector 视窗中点击 Static 右侧的下拉箭头,选择 Contribute GI 选项,在弹出的对话框中选择“Yes, change children”选项,从而更改所有子物体的状态。

这样就完成了第二步,即告诉系统哪些物体需要被烘焙。

步骤 3:在 Hierarchy 视图中选中 Lights 的 3 个子物体,然后在 Inspector 视窗中将 Light 下的 Mode 属性设置为 Baked。

步骤 4:在顶部菜单栏中依次选择 Window->Rendering->Lighting,即可打开灯光设置按钮。初次使用时,需要点击 New Lighting Setting,然后注意取消底部 Auto Generate 选项的勾选,点击 Generate Lighting,即可开始烘焙。

注意,烘焙可能需要等一段时间,在编辑器的右下角会有状态提示。

等烘焙结束后,可以看到场景中的灯光发生了细微变化。接下来运行该场景,在 Scene 视图中修改 Directional Light 的角度发现,对象的阴影并不会改变。同样,修改 Point Light 的颜色、范围等发现,场景的光照效果不会有任何改变。

这是因为 Directional Light、Point Light 和场景中所有对象的光照信息都已经烘焙到 Lightmap 中,现在场景中的光照数据来自 Lightmap,而不是根据灯光变化实时计算的。

如果场景中的灯光发生了变化,开发者需要手动再次进行烘焙,这样场景中的光照效果发挥发生变化。

2.2 Lightmap 的使用

在进行灯光烘焙后,场景中的光照信息全部储存在 Lightmap 中。现在,在 Lighting 视图中点击 Baked Lightmaps 查看生成的灯光烘焙贴图。

直接点击上图中的 Open Preview即可查看灯光烘焙贴图的数据。Unity 将通过内置的图片浏览器打开烘焙文件

实际上,在烘焙完成之后,在 Project 视窗中会自动生成一个新的文件夹,其中存放了灯光烘焙文件。

2.3 Light Probe 的使用

当场景中的灯光烘焙之后,光照信息和阴影都变“静止”了。如果场景中有动态的物体,比如可以自由行走的玩家,那玩家岂不是没有阴影了?这个问题就需要用到 Light Probe 和 Reflection Probe 来解决。

步骤 1:在场最中添加一个 Cube 对象,不做任何设置,重新进行一次灯光烘焙。目前场景中的灯光效果如下图所示。

新添加的 Cube 对象呈灰色,并且没有任何阴影,显然它没有受到任何光照。点击 Play 找钮运行场景,然后切换到 Scene 视图将它从一端移动到另一端。但是在整个移动过程中,它一直保持灰色,与周围的环境格格不人。

步骤 2:在场景中添加 Light Probe Group(光照探头)。在 Hierarchy 视图中,右键->Light->Light Probe Group命令,即可在场景中添加光照探头。将灯光烘焙后,运行场景并移动新添加的 Cube 到光照探头范围内,发现其不再是灰色,如图 4-54 所示。

Cube 附近的小圆球即光照探头,随着 Cube 的移动,负责工作的光照探头也会随之改变。但仔细观察可以看到,Cube 虽然收到光照的影响,但没有和其他物体一样产生影子。也就是说,目前场景中的光照依然是存在问题的。对于这种现象,有两种解决方法,一种是更改新添加的 Cube 对象的 Static 状态为 Contribute GI,另一种是更改光源的烘焙模式。

在灯光第一次进行烘焙前,将 Directioanal Light 和 Point Light 都设置为 Baked。Baked 模式下的灯光只会作用于标记为 Contribute GI 的对象。除了 Baked(烘焙)和 Realtime 实时模式外,Unity 还提供了第三种模式 -- Mixed

Mixed 模式下的灯光依然会被烘焙标记为 Contribute GI 的对象,同时作用于场景中的其他对象。

步骤 3:将 DIrectional Light 和 Point Light 模式都设置为 Mixed,如图所示

这是因为 Mixed 模式包含 Baked 和 Realtime 两种模式的全局光照,对标记为 Contribute GI 的对象进行烘焙,而对没有被标记为 Contribute GI 的对象依然采用 Realtime 模式实时渲染。

所以,如果游戏场景中的灯光需要进行烘焙,必须在玩家或敌人移动的范围内布置光照探头,这样才能尽可能逼真地模拟光照效果。设置完成后再次进行灯光烘焙,可以看到新添加的 Cube 对象虽然没有更改 Static 状态,但也能产生影子。

添加光照探头时,Lighting 下还有另外一个选择 -- Refleciton Probe(反射探头)。光照探头的作用是收集附近的光照信息,并对探头照射范围内的对象进行实时渲染。反射探头的作用是收集附近的反射信息,并反射到探头照射范围内的对象上,以便提升场景中灯光模拟效果。

为了更深入得了解反射探头,用一个新的示例来说明。

步骤 1:创建一个新场景,将其命名为 ProbeTestScence。在 Project 视窗中右键单击 Assets 文件夹,新建子文件夹,并将其命名为 Materials。在该文件夹下,右键->Create->Material,创建一个新的 Material 文件,将其命名为 GreenMaterial。

步骤 2:在 Project 视窗种点击刚刚创建的材质文件,在 Inspector 视图中设置 Albedo 颜色为绿色

步骤 3:使用相同的方式再创建一个 Albedo 颜色为红色的材质文件。接下来在场景中创建两个新的 Cube,并拉伸成长方体,随后点击其中一个 Cube,将绿色材质球拖放到 Inspector 视图中,即可将这个 Cube 对象设置为绿色。使用相同的方式将另外一个 Cube 设置为红色。

步骤 4:在场景中添加一个Reflection Probe,设置 Type 为 Realtime,设置 Refresh ModeEvery Frame,这样反射效果就会在每帧都更新。将反射探头放置于两个 Cube 之间,如下图所示

步骤 5:目前反射效果并不明显,我们需要提升反射探头的反射强度,如图下所示

步骤 6:逐步提高 Intensity 的数值,随着数值越来越大,反射效果也越来越明显。当反射强度提升到 5 时,可以看到绿色 Cube 上反射出了红色,如下图所示