Unity5中叹为观止的实时GI效果
今天为大家分享unity与Alex Lovett共同使用unity5制作的Shrine Arch-viz Demo,其中充分利用了Unity5的实时全局光照功能。实在是太过惊艳,随便一帧都可以直接拿来当做屏保~~~
上面的Demo使用Unity5.2制作,没有导入任何第三方资源包,从无到有历时8周。
全实时光照的场景
整个Demo全是实时全局光照(下简称GI),没有使用烘焙。场景包含了一天的各个时刻、大量集合图形、100来个晚间出没的点光源、一些流光、一些固定支架上的探照光以及可选的闪光灯。昼夜交替的效果是使用天空盒同步太阳运动来实现的,以便捕捉不太明显的光照变化。在Demo中,你可以通过UI自己控制上述所有的光照。
下面欣赏一下Demo的画面:
这个场景对光照有特别要求。有些地方在白天或日落后都只依赖反射光来照亮。
整个实时GI系统是通过预先计算场景中所有静态物体之间的光线路径来实现的。这样一来就可以实时调整光照而不会中断,因为系统已经有了计算光照变化序列的所有信息。但这也意味着场景中的静态物体不能移动,因为动过之后需要重新计算所有路径。所以整个制作过程都是先制作几何体然后添加光照(必要时重复)。不经意地移动几何体并同时调整光照可能会导致多次计算光照信息。
实时GI创作
上面的效果很震撼吧,但这只能在PC或主机上运行,要想在移动端运行还得经过一些改造。
Unity5中的实时GI利用了Geomerics公司的Enlighten技术,并专为游戏设计。所有的光照计算都是由CPU工作线程异步执行的,由于游戏通常是GPU绑定的,额外的CPU工作对整体帧率的影响很小。而且也只有那些有光照变化的区域需要重新计算。
游戏中的光照延迟是由选取的实时间接光照贴图分辨率决定的。而Alex为了保证响应迅捷,在Demo中将分辨率设得相当低,即便这样还是有些间接光照的地方并未完全达到想要的效果。
间接光照贴图分辨率如下:
- 中央区域两单元一个纹理像素(即每单元0.5纹理像素)
- 沙漠里接近中央区域的位置10单元一个纹理像素
- 沙漠外部区域32单元一个纹理像素
为了使分辨率平均,整个场景采用了平均每个单元0.25纹理像素的做法,下图分别是UV图(表示间接光照贴图分辨率):
Clusters(负责反射光的发射):
反射光贴图:
以及光照方向贴图:
保证光照贴图UV的效果比较重要。某些情况下要非常小心以确保模型在编辑器模式和运行模式下都能正常工作。典型的例子就是楼梯。
楼梯照明表现正常是有难度的,因为尺寸较大的纹理像素可以覆盖多个阶梯。这会导致光照层次夹在阶梯之间。另一方面,由于性能问题也不能为阶梯使用多个纹理像素。此场景中的楼梯是有斜面的,它会浪费很多纹理像素空间。最初楼梯实时GI的UV布局如下:
其中使用了70x72纹理像素的光照贴图。这个布局有两个问题,首先是每个阶梯用的纹理像素太多(4x4);其次被分离到两张图中的斜面也会占用至少4x4的纹理像素。
Enlighten在运行时处理纹理是以2x2的单位来优化的,所以每张图需要至少2x2的纹理像素。另外,Enlighten包含了无缝衔接功能,图片会自动被衔接到一起以便保证图像更平滑,例如球体和圆柱。这个功能要求各图在边缘部分有单独的方向信息。方向信息只保存在各个块中,所以衔接过的图需要至少2x2个块,加起来就是4x4的纹理像素。此楼梯中不需要进行衔接,所以2x2的纹理像素就可以满足需求。
在光照面板中新加入了最小图尺寸的选项:
这个值可以设为4用于衔接用到方向的贴图,或者设为更紧凑的2,可以大大降低纹理像素密度,这时楼梯模型只需44x46纹理像素的光照贴图即可:
此时楼梯斜面还是霸占了一些不必要的空间。下图展示了模型的UV边界。注意整个斜面集成到了阶梯中:
光照贴图UV的2D视图中并未出现斜面,因为斜面被整个叠在了阶梯中。这样做的目的是避免光照模拟会考虑到被遮挡的斜面。
斜面之所以被分为两张图是因为用于实时GI的UV会按照各对象用到的实际分辨率重新打包。打包算法会默认各图会有0.5纹理像素的边界,这样可以避免图像失真。也保证了对图集的充分利用,同时不需为各具体UV打包边缘。
边缘越锋利带来的问题越明显,此例中楼梯与斜面的边缘就是如此。模型导入器会自动复制锋利边缘处的顶点,因为边缘两边需要不同的法线。所以贴图会在外部分离。重新打包会导致斜面贴图分离。因为贴图默认使用法线来检测。当它找到顶点位置、UV完全一样但法线不同的边缘,就会按照边缘分离贴图。
此例中不需如此。斜面会被集成到阶梯的贴图中,因为它对光照没有实际影响。所以为了实现这点,同样在光照面板中新增了Ignore Normals,以便在打包阶梯期间进行贴图检测时忽略法线。这样可以聚合UV贴图而不用在乎边缘:
选中Ignore Normals会进一步减小纹理像素密度。此时斜面已被集成到楼梯中。最终的光照贴图是22x24纹理像素:
使用这些选项可以将实时GI预计算的时间从1.5小时直接缩短至15分钟。
关于性能
实时GI在满足其运行性能与内存需求的情况下不需要太多调整。然而,后期特效特别多,其中包括Filmic Vignette(电影修饰), Bloom(爆发),Tonemapping(色调映射),Lens Distort(镜头变形),Screen Space Ambient Occlusion(屏幕空间的环境光遮蔽),Color Correction Curves(颜色校正曲线),Noise And Grain(噪声及噪点),Color Grading Properties(颜色渐变)以及 Antialiasing(抗锯齿)等等。除开这些可以在PC上运行时达到60fps。
快速环境光
实时GI系统可以使用天空盒直接驱动环境光输入。但使用该功能需要先从GPU获取天空盒纹理以便基于实时GI系统更新CPU。这在环境光每帧都会发生变化的情况下非常不理想。所以替代方法是,由当前时间来控制环境光,并转换为关照渐变和环境光强度用于驱动实时GI系统。这些都可以在光照面板中设置:
渐变环境光源可以完全由CPU来处理。这样处理的效果与完全使用天空盒几乎看不出差别。
用于更新环境光的核心代码如下:
[AppleScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
usingSystem; usingUnityEngine; usingSystem.Collections; usingSystem.Collections.Generic; [ExecuteInEditMode] publicclassEnvironmentUpdater
:
MonoBehaviour { publicGradient
groundGradient ,
equatorGradient ,
skyGradient; privateTimeOfDayManager
m_TimeOfDayManager; voidOnEnable
( )
{ m_TimeOfDayManager
=
FindObjectOfType < timeofdaymanager >
( ) ; } voidUpdate
( )
{ floatcurrentTime
=
m_TimeOfDayManager.time; RenderSettings.ambientGroundColor
=
groundGradient.Evaluate ( currentTime ) ; RenderSettings.ambientEquatorColor
=
equatorGradient.Evaluate ( currentTime ) ; RenderSettings.ambientSkyColor
=
skyGradient.Evaluate ( currentTime ) ; } } |
快速发射光
与环境光类似,实时GI系统可以根据发光着色器属性直接驱动发光物体。默认会使用发射光材质属性,或添加自定义着色器通道,在实时光照贴图空间渲染出一张发射光贴图。这需要在实时GI系统消灭数据之前从GPU下载纹理。可以在脚本中使用(DynamicGI.SetEmissive)将物体的发射光属性设为固定的颜色,并允许实时GI系统在模拟GI中直接使用发射光。这样可以完全绕过GPU,且几乎不需要什么代价就可以照明场景。
关于大场景中的GI
可以对大场景采取流式加载以保证内存消耗低同时快速响应。实时GI系统可以结合LoadLevelAdditive和UnloadLevel使用。当然在加载关卡时有些注意事项,因为关卡并非加载后直接可见,所以可能会影响到反射光。
其它的加载方式,或多场景编辑的功能会在12月8日发布的Unity5.3中提供,那时通过实时GI系统缩放场景中的光照就很容易了。
今天为大家分享unity与Alex Lovett共同使用unity5制作的Shrine Arch-viz Demo,其中充分利用了Unity5的实时全局光照功能。实在是太过惊艳,随便一帧都可以直接拿来当做屏保~~~
先奉上视频:
上面的Demo使用Unity5.2制作,没有导入任何第三方资源包,从无到有历时8周。
全实时光照的场景
整个Demo全是实时全局光照(下简称GI),没有使用烘焙。场景包含了一天的各个时刻、大量集合图形、100来个晚间出没的点光源、一些流光、一些固定支架上的探照光以及可选的闪光灯。昼夜交替的效果是使用天空盒同步太阳运动来实现的,以便捕捉不太明显的光照变化。在Demo中,你可以通过UI自己控制上述所有的光照。
下面欣赏一下Demo的画面:
这个场景对光照有特别要求。有些地方在白天或日落后都只依赖反射光来照亮。
整个实时GI系统是通过预先计算场景中所有静态物体之间的光线路径来实现的。这样一来就可以实时调整光照而不会中断,因为系统已经有了计算光照变化序列的所有信息。但这也意味着场景中的静态物体不能移动,因为动过之后需要重新计算所有路径。所以整个制作过程都是先制作几何体然后添加光照(必要时重复)。不经意地移动几何体并同时调整光照可能会导致多次计算光照信息。
实时GI创作
上面的效果很震撼吧,但这只能在PC或主机上运行,要想在移动端运行还得经过一些改造。
Unity5中的实时GI利用了Geomerics公司的Enlighten技术,并专为游戏设计。所有的光照计算都是由CPU工作线程异步执行的,由于游戏通常是GPU绑定的,额外的CPU工作对整体帧率的影响很小。而且也只有那些有光照变化的区域需要重新计算。
游戏中的光照延迟是由选取的实时间接光照贴图分辨率决定的。而Alex为了保证响应迅捷,在Demo中将分辨率设得相当低,即便这样还是有些间接光照的地方并未完全达到想要的效果。
间接光照贴图分辨率如下:
- 中央区域两单元一个纹理像素(即每单元0.5纹理像素)
- 沙漠里接近中央区域的位置10单元一个纹理像素
- 沙漠外部区域32单元一个纹理像素
为了使分辨率平均,整个场景采用了平均每个单元0.25纹理像素的做法,下图分别是UV图(表示间接光照贴图分辨率):
Clusters(负责反射光的发射):
反射光贴图:
以及光照方向贴图:
保证光照贴图UV的效果比较重要。某些情况下要非常小心以确保模型在编辑器模式和运行模式下都能正常工作。典型的例子就是楼梯。
楼梯照明表现正常是有难度的,因为尺寸较大的纹理像素可以覆盖多个阶梯。这会导致光照层次夹在阶梯之间。另一方面,由于性能问题也不能为阶梯使用多个纹理像素。此场景中的楼梯是有斜面的,它会浪费很多纹理像素空间。最初楼梯实时GI的UV布局如下:
其中使用了70x72纹理像素的光照贴图。这个布局有两个问题,首先是每个阶梯用的纹理像素太多(4x4);其次被分离到两张图中的斜面也会占用至少4x4的纹理像素。
Enlighten在运行时处理纹理是以2x2的单位来优化的,所以每张图需要至少2x2的纹理像素。另外,Enlighten包含了无缝衔接功能,图片会自动被衔接到一起以便保证图像更平滑,例如球体和圆柱。这个功能要求各图在边缘部分有单独的方向信息。方向信息只保存在各个块中,所以衔接过的图需要至少2x2个块,加起来就是4x4的纹理像素。此楼梯中不需要进行衔接,所以2x2的纹理像素就可以满足需求。
在光照面板中新加入了最小图尺寸的选项:
这个值可以设为4用于衔接用到方向的贴图,或者设为更紧凑的2,可以大大降低纹理像素密度,这时楼梯模型只需44x46纹理像素的光照贴图即可:
此时楼梯斜面还是霸占了一些不必要的空间。下图展示了模型的UV边界。注意整个斜面集成到了阶梯中:
光照贴图UV的2D视图中并未出现斜面,因为斜面被整个叠在了阶梯中。这样做的目的是避免光照模拟会考虑到被遮挡的斜面。
斜面之所以被分为两张图是因为用于实时GI的UV会按照各对象用到的实际分辨率重新打包。打包算法会默认各图会有0.5纹理像素的边界,这样可以避免图像失真。也保证了对图集的充分利用,同时不需为各具体UV打包边缘。
边缘越锋利带来的问题越明显,此例中楼梯与斜面的边缘就是如此。模型导入器会自动复制锋利边缘处的顶点,因为边缘两边需要不同的法线。所以贴图会在外部分离。重新打包会导致斜面贴图分离。因为贴图默认使用法线来检测。当它找到顶点位置、UV完全一样但法线不同的边缘,就会按照边缘分离贴图。
此例中不需如此。斜面会被集成到阶梯的贴图中,因为它对光照没有实际影响。所以为了实现这点,同样在光照面板中新增了Ignore Normals,以便在打包阶梯期间进行贴图检测时忽略法线。这样可以聚合UV贴图而不用在乎边缘:
选中Ignore Normals会进一步减小纹理像素密度。此时斜面已被集成到楼梯中。最终的光照贴图是22x24纹理像素:
使用这些选项可以将实时GI预计算的时间从1.5小时直接缩短至15分钟。
关于性能
实时GI在满足其运行性能与内存需求的情况下不需要太多调整。然而,后期特效特别多,其中包括Filmic Vignette(电影修饰), Bloom(爆发),Tonemapping(色调映射),Lens Distort(镜头变形),Screen Space Ambient Occlusion(屏幕空间的环境光遮蔽),Color Correction Curves(颜色校正曲线),Noise And Grain(噪声及噪点),Color Grading Properties(颜色渐变)以及 Antialiasing(抗锯齿)等等。除开这些可以在PC上运行时达到60fps。
快速环境光
实时GI系统可以使用天空盒直接驱动环境光输入。但使用该功能需要先从GPU获取天空盒纹理以便基于实时GI系统更新CPU。这在环境光每帧都会发生变化的情况下非常不理想。所以替代方法是,由当前时间来控制环境光,并转换为关照渐变和环境光强度用于驱动实时GI系统。这些都可以在光照面板中设置:
渐变环境光源可以完全由CPU来处理。这样处理的效果与完全使用天空盒几乎看不出差别。
用于更新环境光的核心代码如下:
[AppleScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
usingSystem; usingUnityEngine; usingSystem.Collections; usingSystem.Collections.Generic; [ExecuteInEditMode] publicclassEnvironmentUpdater
:
MonoBehaviour { publicGradient
groundGradient ,
equatorGradient ,
skyGradient; privateTimeOfDayManager
m_TimeOfDayManager; voidOnEnable
( )
{ m_TimeOfDayManager
=
FindObjectOfType < timeofdaymanager >
( ) ; } voidUpdate
( )
{ floatcurrentTime
=
m_TimeOfDayManager.time; RenderSettings.ambientGroundColor
=
groundGradient.Evaluate ( currentTime ) ; RenderSettings.ambientEquatorColor
=
equatorGradient.Evaluate ( currentTime ) ; RenderSettings.ambientSkyColor
=
skyGradient.Evaluate ( currentTime ) ; } } |
快速发射光
与环境光类似,实时GI系统可以根据发光着色器属性直接驱动发光物体。默认会使用发射光材质属性,或添加自定义着色器通道,在实时光照贴图空间渲染出一张发射光贴图。这需要在实时GI系统消灭数据之前从GPU下载纹理。可以在脚本中使用(DynamicGI.SetEmissive)将物体的发射光属性设为固定的颜色,并允许实时GI系统在模拟GI中直接使用发射光。这样可以完全绕过GPU,且几乎不需要什么代价就可以照明场景。
关于大场景中的GI
可以对大场景采取流式加载以保证内存消耗低同时快速响应。实时GI系统可以结合LoadLevelAdditive和UnloadLevel使用。当然在加载关卡时有些注意事项,因为关卡并非加载后直接可见,所以可能会影响到反射光。
其它的加载方式,或多场景编辑的功能会在12月8日发布的Unity5.3中提供,那时通过实时GI系统缩放场景中的光照就很容易了