Unity 预计算实时GI

2023-11-02 16:50
文章标签 实时 unity 预计 gi

本文主要是介绍Unity 预计算实时GI,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

(一)介绍

  在Unity中有两种区别很大的技术被用于计算全局光照GI或光源反射,它们就是烘焙全局光照(Baked GI)和预计算实时全局光照(Precomputed Realtime GI)。本文主要介绍预计算实时全局光照(PRGI),学习如何使用Unity全局光照计算系统 — Enlighten系统来优化一个场景,让预计算只需要几分钟而非几小时。

为什么要使用PRGI

当启用PRGI时,光照预计算指的是计算静态几何对象周围光的反射,并存成数据给Runtime执行使用的过程。这个过程减少了原本必须在Runtime执行时的光照计算量,可以让项目在保持FPS稳定的同时,还允许使用实时反射光照。

当启用烘焙GI(Baked GI)时,预计算的过程会计算并产生传统的光照贴图(Lightmap),这些贴图会以资源(Assets)的形式存在项目中,但是无法在Runtime执行时被更改。而PRGI则是使用不同的方法生成光照贴图, PRGI计算的结果会被存成光照数据文件(Lighting Data Asset),这些数据可以在Runtime执行时实时产生和更新一组低分辨率的光照图。

不过,除非场景已经为PRGI计算优化过,否则完成这些计算所需的时间可能会很长。下面我们一起来看一下如何针对Enlighten系统来做场景优化,从而把预计算时间从几小时缩小到几分钟。我们将会涵盖以下几个要点:

如何定义适合当前场景的光照分辨率
了解什么是光照图(Charts),以及它是如何影响计算时间的
如何开始预计算
使用光照探针来降低光照方案的复杂度
改进由Unity PRGI自动拆解出的UV
了解何谓集群(Clusters),以及如何用它来产生GI照明
了解如何使用光照贴图参数来微调单个游戏对象上的光照

一旦我们学习并了解了这些技术,我们就可以充分利用PRGI的优点: 快速的光照迭代,以及在打光过程中和在游戏过程中对实时反射光照进行快速试验的能力

PRGI优化演示
在下图所示的场景里,用没有优化过的默认设置,在我们的测试机上预计算光照大约花了7.5个小时。显然这是无法接受的!


无优化场景(上图) :预计算7.5小时,有优化场景(下图):预计算2.25分钟

使用本教学所介绍的技术进行大约30分钟的场景准备后,这个场景的预计算仅仅花了2.25分钟就得到了产品等级的效果。这个技术的好处是显而易见的,因为我们可以在游戏过程中快速迭代场景照明而不需要重新计算GI和改变GI照明。

学习前准备
您可以在 Asset Store资源商店上下载官方光照优化素材包 ,跟着我们进一步学习。本教学所用的是Scenes/Article目录下的范例场景:
LightingTutorialOptimal:展示PRGI如何设定能用最短的时间呈现出产品等级的结果。
LightingTutorialNonOptimal:呈现了我们在项目中常看到的问题,要么造成无法烘焙,要么花费无法接受的时间烘焙,这是一个很好的错误示范。
LightingTutorialStart:我们在教学过程中用到的场景,我们会按照步骤一步一步将它变成一个产品等级的结果。

)实时分辨率

如何调整实时分辨率?
当使用PRGI来计算场景照明时,首先要做出的决定之一是确定场景的实时分辨率(Realtime Resolution),因为它决定每个世界单位所使用即时光照的贴图像素数量。Realtime Resolution设定可以在Lighting窗口中找到并调整,方法如下

Unity的Lighting窗口:主要显示整个场景会用到的光照设定。

  • 通过Window > Lighting菜单,开启Lighting窗口,并选择Scene标签页。
  • 确保Precomputed Realtime GI选项打上勾,在它下面找到Realtime Resolution属性,即可设置它的值。

但这个分辨率应该如何设置呢?

选择适当的分辨率
在设定场景时,首先要要了解你的项目使用的单位比例,可能在你的项目里一个单位等于现实世界的1米、一英尺或一公分。Unity没有默认真实世界的单位,所以要由开发者自行决定。

在这个范例里我们采用1 unit(单位) = 1 meter(米)。因为这和Unity的物理概念不谋而合。举例来说Unity的重力预设就是以每秒多少单位来计算的,因此设定1单位 = 1米对于一个真实世界的场景来说是恰当的。

Realtime Resolution的值通常可以由游戏世界的规模来制定 。例如,你的场景是一个 拥有丰富光照变化的室内环境 。在这种情况下,高一点的值比如2 - 3,可以捕捉到更详细或"高频"的光照。

如果你的场景是一个 规模较大的户外环境 ,可能覆盖了几百甚至上千单位(平方米)的表面,而这些表面并不会剧烈地改变反射光的颜色。在这样的情况下,把适合计算复杂室内场景的设定用在有大量相同特征的室外环境是很浪费的。我们会浪费宝贵的CPU时间和内存去更新那些对整体外观贡献不大的光照贴图。为了教学目的,我们会提高PRGI计算期间必须考虑的光照贴图的像素数量,这会对预计算的时间造成很大的影响。 针对室外环境,可以为场景中尺寸较大的游戏对象设置0.5 - 1的texel,针对地形可以设置0.1- 0.5的texel

Unity PRGI所需要的Realtime Resolution值比传统光照贴图密度要小好几个等级,这是因为我们只从这些光照图里获取间接光数据,且这些数据通常分辨率都很低。所以使用PRGI时,清晰的阴影通常都是通过实时运算而非高分辨率的光照图来提供的。

在这里使用传统光照惯用的值,例如:30 texels,可能会导致预计算失败或无法计算。室内场景比较适合的值是2 - 3,室外场景则是0.5 - 1。这是我们在使用1单位 = 1米的前提下,如果单位大小不同这些值就需要调整。

具体可以参考以下的场景与Realtime Resolution值对照表(1单位代表1米):

室内:2-3 像素/单位
户外:0.5-1 像素/单位
地形:0.1-0.5 像素/单位

当设定场景实时分辨率时,Unity会指定给场景内的静态对象。新建的带有Mesh Renderer且标记为静态光照(Lightmap Static)的对象,会使用这个值一直到它被其他设置修改。

除了帮场景加上分辨率设定外,我们还能针对每个对象调整光照贴图的分辨率,在需要高分辨率来提供更高真实感的情况下,我们可以选择性的提高这个值。通常是将场景里最多的对象分辨率设为默认值,然后手动调高需要更多照明细节对象的值。

练习一下
如果你想跟着做,打开 教学素材包 里的LightingTutorialStart场景。

在范例场景里,我们有一个户外环境,具有中等大小的地形与相当一致的色调。所以我们设定Realtime Resolution为0.5像素/单位就足以处理从场景其他对象来的光照反射。然而,场景里也有些贴图细腻的木屋。由于场景里有比地形对象还多的房子,我们应该把默认的分辨率设定为适合房子的数值。然后我们可以单独修改地形对象的分辨率,这样可以降低场景准备的工作量。考虑到这点,我们用1作为我们的默认分辨率,操作步骤如下:
  • 开启Lighting窗口(Window > Lighting) 然后选择Scene页签
  • 设定Realtime Resolution值为1

由于我们的世界单位设定为1单位 = 1米,这表示由PRGI所产生的光照图将会是1x1米的大小,或许你看起来很低,那是因为我们只抓取间接光照。还会从场景里的直接光源计算清晰的阴影和反光

三)了解光照图

光照图是什么?
在Unity PRGI里,一个光照图(Chart)是表示一个光照贴图(Lightmap)的区域,用来映射场景对象的光照贴图UV。你可以认为它是能影响对象的一张瓦片图。一张光照图由两部分组成:辐照度(光照)和方向性(主要光线方向编码)。

当产生PRGI数据时,图表里的每个texel都会被计算光照。换句话说,场景里面有大量的光照图可能会增加PRGI耗时的主要原因之一,因此理解光照图的运作原理以及如何管理它们来优化光照计算的时间是很重要的。

光照图的尺寸
最小的4x4 texel的UV光照图

如上图所示,这是最小的4x4单位的UV光照图,为了防止贴图过滤造成的渗色,UV贴图始终被光照图包住并空出一半texel的宽度。

在预设的情况下,每张光照图最少要有4x4 texel的大小,所以说不管世界中的物体或对应的UV有多大,一张图表至少需要16个texel。因此如果有一个1x1米的对象并带有一个图表,间接分辨率是1,那这个对象就会需要16个texel来表现。最后Unity会将这些图表缝合在一起,作为模型边缘的光线取样参考。Unity需要每张图表的边缘至少有4个texel,方便在缝合不同光照图时有对照的依据。

要注意的是使用实时GI时不需要自己去填这些UV的边,因为Unity在处理网格汇入的流程时,会自动将光照UV图向内挤出一个边框的空间。这样就能让相邻的图表无需做渗色处理,也能保持双线性插值(Bilinearly Interpolated),能节省宝贵的光照贴图空间。

光照图如何影响计算时间?

要弄清楚这个问题,我们可以试想一下: 1x1对象如果带有50张光照图,Unity会为它创建800个texel。尽管这个数字看上去不是很大,但说明了当图表的量一大就会快速拉高texel的量。越多的贴图像素意味着更为繁重的光照运算要处理,更多的光照数据要计算、压缩和储存,这些因素都会提高场景的复杂度,并导致运行时长时间的计算和较低的效率。

不合理的光照图规划一般是导致计算耗时过长或无法完成的主因。也因为如此,我们许多减少计算时间的方向大多都是思考如何降低场景内光照图的数量。
(四)开始预计算  

计算全局光照需要启动PRGI流程,在开始预计算之前,我们必须确保场景里面至少必须有一个对象被标为静态(Lightmap Static)。 

可以通过层级视图(Hierarchy)的功能,您可以轻松管理并选择哪些对象要用于计算光照。 管理这些对象有非常多的方法,但这不是今天教学讨论的目的。但这里需要,重点指出的就是:好的对象管理流程可以大大提升对象利用率。 

在本文示例中,我们会将所有对象放在一个名为"Environment"的父对象下方进行管理,这个群组会放所有可见的,并且带有MeshRenderer组件,用来组成场景环境的静态对象。 在它下方还会有一些放有各种场景对象的子群组。 

对场景中的对象进行分类有助于频繁重复选择对象的操作 

最好的做法是先从Lighting设置中确定初步大方向,然后边做边调整细节设置:
        •        选择场景里的"Environment"对象
        •        勾选检视面板(Inspector)右上方的Static
        •        如果系统跳出对话框,提示是否要将该对象下的子对象也一起标记为静态对象,请选择”Yes, change children"。 这样就能确保所有在Environment里的对象都变为静态对象并纳入光照计算。

现在场景内有了一些静态对象之后,我们就可以开始计算GI了,步骤如下:
        •        打开Lighting界面(Window > Lighting)然后选择Scene标签页
        •        确认勾选界面最下方的Auto(最下方Build按钮旁,如果打勾Build按钮会灰掉)
        •        然后系统就会开始计算光照。 画面的右下角会出现一个蓝色的进度条,显示目前的工作(几分之几)以及需要计算的工作量。
        •        如果未勾选Auto,也可以手动点击Build进行计算,系统会先询问是否要存档后开始计算光照。 本次教学建议直接勾选Auto即可。

 
进度条显示目前进度,并提示等待计算的工作量 
(五)光照探测

何谓光照探测
光照探测是一种快速计算实时渲染应用中的光照技术,通常会用于处理游戏世界的人物角色或是动态物体的光照,它的优点在于运行时有不错的处理性能而且预计算也相当快速。

光照探测的原理是通过放置在3D空间里的探针来接收光照信息,然后用类似球谐函数(spherical harmonics)的数学方法将信息编码在一个球体上。这些系数占用空间很小,在游戏运行时能快速”解包",以便Shader可以存取并计算表面光照,在Unity中这个功能叫做光照探针(Light Probes)。它是一种让对象接收场景间接照明的好方法,虽然被光照探针指定的对象无法计算场景的光照反射,但通常影响不大。

使用光照探测是有些限制的,其中一个限制:在不提高探针数量的前提下很难在球形范围上表现出高频或斑驳的光照,并且其精度和消耗成本成正比。也就是说,在考虑性能的前提下,必须限制使用较低阶的球谐函数。

实际上,一个3D坐标只能用一个球体来记录光照信息,所以光照探测不适合用于有大量光照投射在大物体表面的情况,所以不得不提到另外一个限制:当用球谐函数在一个球体上进行编码时,通常不擅于处理拥有特大平面的物体或是带有很深的凹洞的物体。如果想要将光照探测技术用于大型物体,可以参考Unity手册中Unity提供的另一个光照探测代理体(Light Probe Proxy Volumes, LPPV)技术做进一步了解。

尽管有这些限制,光照探测还是很适用于和符合条件的小物体一起搭配使用,以较低的成本消耗发挥出卓越效果。 我们后续将深入探讨如何设置及摆放光照探针(Light Probes),现在只了解适当使用光照探测功能可以降低光照贴图的数量。

为对象设置光照探针
尽管我们一般将环境里的对象都设置为静态后再做光照计算,但其实整个环境里有很多对象都适合用光照探测技术进行处理。减少场景里光照图的数量是加快预计算时间的关键,将对象的静态标签清除之后,它们不再受到PRGI计算的影响的同时,光照贴图的数量也会降低。 


 
使用光照探测的最佳物体就是像这种凸起的小碎石

从Unity编辑器的层级视图(Hierarchy)看Environment下的子对象,会发现Props下的对象几乎都是点缀场景用的小物体,例如:石头、水桶或木板。 这些物体非常多,而且很多小物体的UV可能不太好展开。获取不失真的光照贴图UV可能会产生很多UV Shell,一旦UV Shell过多就会需要额外的光照图,更多的光照图代表将产生更多的贴图像素。

假设这些物体很小,不太可能对场景的间接照明产生什么影响,而且它们表面能显示的光照细节也有限,所以这样的物体非常适合用光照探测技术来计算,不但可以加速预计算时间,同时要存到内存中的光照图和Shader解码的数量也变少了,在运行时的性能也会因此提升。具体操作如下:
  • 从层级视图(Hierarchy)选择Props对象
  • 在检视面板(Inspector)中取消勾选最右上角的Static
  • 在弹出的提示框中,选择"Yes, change children"


这时等待计算完成,就能看到场景最终的光照计算结果,你会发现被探测光影响的非静态物体(non-Static objects)和周围的物体有些格格不入,和场景的光照也不匹配。 这是因为我们还未设置光照探针(Light Probes),导致这些物体被纳入环境探针(Ambient Probe)的光照计算。 环境探针本质上是一个在场景里无法看到的隐形探测器,只对Lighting视图中的Ambient Source所指定的来源进行采样。

 
在没有光照探针影响下,非静态物体的位置可能看起来有些异样

为了让非静态物体的摆放位置更为合理,我们需要花点时间来将光照探针放置在场景的周围,方便对世界中的间接照明进行采样。

放置光照探针
非静态物体会通过附近的光照探针接收光照信息,光照探针会基于探针之间的空间划分四面体,然后检查物体属于哪个四面体来决定物体"读取"哪个探针。 为了正确地产生这些四面体,探针之间的位置关系在3D空间里就必须要放得有体积感。

 
画面中那些白色的球体就是光照探针在场景里的样子 

光照探测技术执行的消耗非常低,预计算也非常快。然而考虑到性能,也有些需要注意的地方:尽管设置简单,但密度太高的探针可能会浪费资源,因为太接近的探针在特定光照条件下的采样结果没什么差异。 最好的做法是在光照变化明显的区域放置密度较高的探针,例如由亮转暗的区域,或是强光反射的区域。设置光照探针的具体方法如下:
  • 从GameObject菜单新建一个光照探针群组(GameObject > Light > Light Probe Group)
  • 开始放置光照探针,从层级视图(Hierarchy)选择刚才新建的光照探针群组对象
  • 在检视面板(Inspector)中找到Light Probe Group组件,选择Edit Light Probes开始编辑
  • 可以从场景直接点选那些探针,留下角落的一个光照探针,其他可以全部删除
  • 将剩下的一个光照探针移到地形上方,然后按住Ctrl + D( Mac是按Cmd + D)键复制
  • 用移动工具(按W键)将复制的二号光照探针沿着Y轴往上约2米(2 meters)
  • 再复制一次光照探针,并将新光照探针的Y轴上移更高,约5米

 
Light Probe Group组件的Edit Light Probes按钮位置


这样的垂直光照探针分布为的是可以同时采样到地面、头部高度和天空的间接光照(Indirect Light)。 当我们开始将这组光照探针大量复制到场景其它位置来产生光照探测区域时,要确保可玩区域都会被探针所产生的四面体覆盖到,可以看到光照探针之间会以品红色的线串联表示这些区域。

 
光照探针的位置和它们之间所产生的四角面
  • 全选刚刚建立好的三个光照探针(按住Shift并点选每个光照探针或是拖拽进行框选)
  • 复制整个光照探针组并移到场景其它需要采样的地方


需要采样的地方不外乎是有阴影的区域或是地形材质色调会变化的地方。请记住,我们的目的是对场景里的间接或反射光照进行采样。 为了让每个光照探针的性能都用在刀刃上,尽量确保它们对一些光照变化明显的地方进行采样,如果光照探针放在一致性很高的光照区域,那光照探针反映给动态物体的结果不会太明显。 如同游戏优化的各方面技巧一样,尽量确保每个光照探针都有其存在的必要。

继续重复上述操作,在照明区域大密度地放置光照探针直到产生一个像笼子般的区域,涵盖所有的可玩区域。 布置这些光照探针时,
请记得每一次都要检查最底部的光照探针要和地面保持一定的距离

在我们的示例LightingTutorialOptimal场景中带有两组光照探针,VillageLightProbeGroup比较密集、用于村庄区域,ExtentsLightProbeGroup比较稀疏、用于村庄外无法到达的区域。 第二个光照探针组涵盖了整个游戏世界以防止任何非静态物体离开可玩区域,这一组不需要像村庄那组探针那么密集,垂直轴方向只要两个探针就够了。

将光照探针拆成两组,方便定义光照探针的用途,每个光照探针组都可以独立开启或关闭,方便适应场景的各种需求,这些光照探针在运行时会自动组合并执行重叠检查(de-duplication pass)以删除所有重叠的探针。

想看到光照探针的照明结果,必须等到预计算完成,如果勾选Lighting视图(Window > Lighting)下的Auto就会自动进行计算,如果未勾选,则需要按下旁边的Build按钮来手动计算。

一旦计算完成后,你会发现场景里面的非静态物体会开始接收光照探针的信息,看起来和场景光源会更匹配。如果要查看照明状态下的光照探针,可以从层级视图(Hierarchy)选择光照探针组。

现在场景里有了光照探针组,我们就能为场景中的小物体取得间接光照信息,而不需要从PRGI的流程中为这些物体生成额外的光照图。 

(六)UV展开与减少光照图数量

UV展开
为什么要讨论UV展开?因为生成光照图(Charts)是为了包裹静态网格渲染器(Static Mesh Renderer)的UV贴图坐标,所以一个对象所需要的光照图数量主要取决于物体需要展开的UV Shell数量。这就涉及到UV展开的问题,它需要保持几何面上贴图像素扭曲度与所需Shell数量之间的平衡。

一般来说Unity的UV展开算法能得到不错的结果,但有时候需要我们手动调整设置,因此有必要了解一下自动UV展开功能背后的原理。下面我们来看看三个UV展开的不同例子。


这样的UV展开不会变形,但需要多张UV Shell

从上图我们可以看到,作为贴图的棋盘网格图像瓦片一样保持比例地贴在立方体表面,并没有变形。试想如果这个棋盘图是一个光照贴图,我们会得到一个视觉正常没有变形的结果,只是需要耗费6个UV Shell。这就表示用Unity PRGI进行计算时也将生成六张光照图,从曾经介绍过的光照图的概念一文中我们了解到,不论何种条件,每张光照图至少都需要4x4 texel来表示,这在还没考虑分辨率之前最少就会消耗96个像素。

 
用单一UV Shell所产生的结果,贴图变形很严重

上图中物体的UV贴图坐标只用一个UV Shell就涵盖了所有范围,虽然这种做法生成的光照图最少,但视觉效果却不理想。 我们会看到物体表面的贴图歪了,而且它们在UV空间里也相互重叠,如果这是一张光照贴图,物体一面的光照可能会错误投射到相反面上。很明显这种UV展开是有问题的。

 
比较理想的结果,单一UV Shell且贴图未变形

上图的结果比较理想,棋盘没有扭曲,网格比例也保持正方形。而且还成功地用一张UV Shell覆盖了物体的所有面。届时通过连接或缝合对应在模型的边缘以匹配位置。

如果从程序化角度考虑,整个UV展开过程做了哪些事情呢?首先我们会通过正交投影(Orthogonal Projection)让UV贴图在物体上生成独立的Shell,然后分析哪些Shell和物体的边缘有相连关系,一旦找到这些边缘,最后把UV Shell的内容移进去并与相邻的Shell缝合起来。

光照图的可视化
在开始优化UV展开流程和光照图之前,我们需要想办法在编辑器中查看它们。光照图是在网格导入流程中的展开阶段(Unwrapping stage)生成的。对于PRGI来说,这些光照图会在预计算的几何阶段(Geometry stage)被打包到不同的图集(Atlas)里,这样做是为了确保它们不会相互重叠。一旦预计算的几何阶段完成之后就会产生可视化数据,我们就能预览光照图。

如果您使用版本控制(Version Control)进行管理,需要注意 这个预览信息只会储存在本机 ,所以在开始查看之前要执行一次预计算。

 
UV Charts模式

如上图所示,UV Charts模式会将场景中不同光照图表示为不同的颜色,光照图分辨率用附在上面的棋盘格表示。在场景视图切换为UV Charts绘制模式时,可以快速查看光照图,我们可以从场景视图(Scene View)左上方的Draw Mode下拉列表中选择UV Charts。如果启用了自动烘焙(Window > Lighting > Auto),也会自动计算UV展开参数的改变并在场景视图中更新。


如上图,Charting模式的预览视图会用不同颜色的格子表示光照图,并用浅蓝色的线表示光照贴图的UV。

当场景较为复杂时,从场景视图中有可能会漏看光照图。这时候我们可以在Lighting视图中使用预览模式查看单个物体使用的所有光照图。这样可以帮我们更精确地评估这些物体的UV展开结果,有助于降低光照图的数量。具体操作如下:
  • 打开Lighting界面(Window > Lighting),选择Object标签页
  • 从层级视图(Hierarchy)中选择要查看的对象。
  • 查看Lighting界面的预览视图,从最上方的下拉列表中选择Charting模式。

对象使用的光照图数量会以不同颜色的方块叠放在对应的浅蓝色UV坐标上。

UV展开参数说明
下面我们一起来看一下几个有助于优化UV展开的设置,所有的设置都是针对单一对象,具体设置方法如下:
  • 打开Lighting界面(Window > Lighting)并选择Object标签页
  • 从层级视图(Hierarchy)中选择要设置的对象

在设置的时候,我们会遇到一些参数,下面就一起来看看。

Auto UV Max Distance(自动最大UV距离)
Unity的UV展开算法会尝试调整不同的Shell,将UV边缘拼接在一起来简化UV贴图。如果加入Shell后还能将UV尺寸保持在Auto UV Max Distance设置的范围内,才会被考虑加入。这个范围是用Unity的世界空间坐标来定义的,在我们的示例里是1米。

 
在Lighting视图的Object标签页下设置Auto UV Max Distance

大多数情况下,预设的0.5就能有很好的效果,但 对于面积较大的物体可能要提高这个值来防止本来应该被缝合的UV贴图被UV展开算法排除在外

增加这个值通常会让物体所需的光照图数量减少,而降低这个值通常有助于解决贴图像素被拉伸的问题 ,当然也会需要生成更多的光照图进行覆盖。 改变这个值后可以使用UV Charts绘制模式查看场景,从覆盖的棋盘图来评估并进行实验以找出一个最好的平衡点。

Auto UV Max Angle(自动最大UV角度)
UV展开的计算当然也会考虑到相邻Shell的角度,Auto UV Max Angle定义了相邻面共享边缘允许的最大角度,并用内角来计算。如果背面的角度大于这个值,就会排除缝合这个UV Shell。

 
在Lighting视图的Object标签页下设置Auto UV Max Angle

提高这个值会让Unity的算法更容易组合UV图 ,我们能通过这个功能来降低单个对象的光照图数量,但如果设得太宽松,有时会出现贴图被拉伸的情况。反之, 降低这个值会造成算法不易将相邻的UV排一起 ,虽然拉伸的情况会减少,但是会生成更多光照图。以同样的方式,我们可以在UV Charts绘制模式下查看棋盘图并尝试出一个最适合的值。

Preserve UVs(保留UV)

在某些情况下,自动展开UV如果无法获得理想效果,可能会导致生成过多的光照图或贴图失真(GI Charts绘制模式可以做拉伸检查)。 在这种情况下可能需要在模型的UV01通道手动创建UV。 这必须要在其它工具中完成。

如果出现这种情况,可以在 Preserve UVs选项让Unity算法强制使用模型UV01通道指定的UV Shell

 
当需要手动保留UV图时,Preserve UVs选项很有用

要注意的是, 这些Shell会被重新打包以节省光照贴图空间 ,它们会被单独解开保留,而非仅仅记录光照贴图内的坐标信息。

使用这个功能时必须小心, 当指定的UV贴图包含大量的UV Shell时,这个功能可能会让预计算的时间加长 ,因为会跳过Unity的UV展开算法,手动保留的UV Shell到时候会全部交给预计算流程。 请注意,最好的结果是尽可能的降低UV Shell和光照贴图数量,并将UV贴图保持在可接受的拉伸范围内。

Ignore Normals(忽略法线)
在某些情况下,网格导入器可能会拆分几何体,这也会影响到光照贴图数量。例如,如果某个网格有非常多的三角面,Unity可以为了性能将它分割成几个独立的子网格。通常这么做是为了符合特定的硬件需求,比如为了减少每个Draw Call中的三角面数量。 拆分通常发生在相邻的网格面之间法向角度有较大变化的区域 ,比如锐角边(Hard Edges)。这样拆分网格的方式会在模型导入流程中执行,在这个过程中,UV Shell也可能会被拆分开来放到不同的光照图,导致生成额外的光照贴图。


 
Ignore Normals选项可以防止在导入模型时对光照图进行拆分

有时通过这种方式对光照图进行拆分并不理想,得到的结果会增加光照图数量并加长预计算的时间,还有可能在照明的接缝处造成不必要的视觉假象。在这种情况下,启用Ignore Normals选项有助于防止光照图在预计算的时候被拆分开来。

请注意,这个选项仅对预计算实时光照(PRGI)有影响,物件被拆分的网格仍然会被保留以用于其它用途。

大场景迅速迭代
复杂的场景可能包含着上百或上千的静态对象,计算这些对象的光照图可能会导致预计算变得很慢,对场景的迭代速度产生负面影响。

当测试研究上述设置时,将物体放到空场景中会很有帮助,这样可以用最小的预计算时间进行快速迭代。测试好设置之后将该设置复制回原本场景里其它的同类型对象,有助于节省时间。具体操作如下:
  • 打开示例中的LightingTutorialStart场景
  • 从层级视图(Hierarchy)中选择Environment > Structures > Houses下的HouseBig02对象。
  • 按下Ctrl + C键复制该对象(Mac是Cmd + C)
  • 按下Ctrl + N键建立一个新的场景(Mac是Cmd + N)
  • 可能弹出提示框询问是否保存,选择Yes可保存,选择No不保存。
  • 在新的空场景里按下Ctrl + V键粘贴对象(Mac是Cmd+V)
  • 打开Lighting界面(Window > Lighting)选择Scene标签页
  • 确保勾选最下方的Auto
  • 选择Object标签页
  • 在最上方左边的下拉列表中选择Charting
  • 拉大预览视图区域来查看对象UV展开的情况

优化预计算时间
当调整UV展开设置时,最好的结果是找到生成光照图最少与失真度最低的参数组合。 请记住,当启用UV Charts绘制模式时,可以从场景中棋盘图的拉伸状况评估失真程度。


上图中,棋盘图案用来可视化光照贴图的分布,这张图呈现出棋盘图和模型的一致性,代表失真度低。


上图中棋盘图在模型表面被拉伸,表示和光照图的分配不一致。

接下来通过教学示例,来实现我们所学的关于UV展开与优化预计算时间的方法:
  • 打开示例中的LightingTutorialStart场景
  • 从层级视图(Hierarchy)中选择HouseBig02对象(Environment > Structures > Houses下)
  • 打开Lighting界面(Window > Lighting)并选择Object标签页
  • 从上方左边的下拉列表中选择Charting
  • 展开预览视图,注意这里显示了UV Shell与对应颜色的光照图数量

用Charting模式查看预设设置下的HouseBig02对象,还有很大的优化空间

HouseBig02是场景里较为复杂的对象,使用了多张光照图,但还可以调整设置来减少光照图的数量。

 
UV展开设置面板

不存在某个固定设置能适用于所有UV展开的情况,Unity的UV展开算法会基于预设值进行适当的决定,但通过一些经验积累,可以微调出更好的效果。

接下来会开始进行预计算,如果将Auto UV Max Distance调整为0.1,经过短暂的计算时间后,我们可以从预览视图看到光照图数量明显增加许多,检查场景视图可以看到贴图只有轻微拉伸,这是好现象。但我们无法接受光照图数量增加过多。 因为光照图过多代表预计算时间会变长,性能也会降低。

但是若将Auto UV Max Distance调整为10,现在问题应该会反过来,光照图的数量会减少,但可以从UV Charts绘制模式中看出,光照图拉伸的问题变得很严重。

 
Auto UV Max Distance值太高可能会导致UV图被拉伸

Unity会尝试将UV Shells合并以最小化光照图的数量,当两个Shell要合并时会把这个值一并列入考虑,如果两个Shell的距离大于这个值,就不会被合并。

所以当调低Auto UV Max Distance值时,被合并的UV Shell就会减少。这代表最后会生成很多单独的光照图。 而当调高这个值时,距离很远的UV Shell都会被考虑合并,就会生成较少的光照图,但相对而言也会让光照图容易产生扭曲。

对于大物体,有时可能需要调高这个值来确保覆盖整个网格面积,相反,如果是小物体,也可以调低这个值来产生更好的效果 。以示例HouseBig02来说,将Auto UV Max Distance设为0.8可以达到较好的平衡。

 
Auto UV Max Distance设为0.8后效果不错。但还可以继续改善

Auto UV Max Angle参数和Auto UV Max Distance一样影响着Unity的UV展开算法的结果,该算法除了会检测距离之外,也会检测相邻网格之间的角度,角度低于这个值才会被合并。角度越小表示检测通过率越低,被合并的UV Shell越少,也就会生成更多的光照图。相反,角度越大表示算法能容忍的角度越大,被合并的UV Shell越多。这和Auto UV Max Distance的情况一样,结果是会减少光照图的数量,但是会出现变形的问题。

如果将Auto UV Max Angle设置为0,从预览视图可以看到所产生的光照图非常清晰,那是因为我们只允许0或更小的角度才考虑合并,这表示没有UV Shell会被合并,每个都会独立生成光照图。

 
设置角度过小会导致生成过多的光照图

接下来试试将角度调大:
  • 从层级视图(Hierarchy)中选择HouseBig02对象
  • 打开Lighting界面(Window > Lighting)选择Object标签页
  • 慢慢调高Auto UV Max Angle,观察预览视图中的效果

可想而知, 光照图的数量会随着角度增加而减少 ,这是因为更多的UV Shell被合并,UV Shell数量降低光照图就会变少。



 
加大角度有助于减少光照图,尤其是在具有圆形表面的物体上

以本例的HouseBig02来说,将Auto UV Max Angle设为93效果最好。请注意预计算完成后减少的光照图数量。


 
从Charting预览看到优化之后的HouseBig02

到这里就可以将测试出来的设置带回原本的场景,请注意,步骤有点复杂:
  • 再次从层级视图(Hierarchy)中选择HouseBig02对象
  • 找到检视面板(Inspector)中的Mesh Renderer组件,点击上面的齿轮按钮后选择Copy Component。
  • 重新打开LightingTutorialStart场景
  • 从层级视图(Hierarchy)中找到原先的HouseBig02的目录
  • 按住Ctrl(Mac按住Cmd)键,然后将所有的HouseBig02一一点选。
  • 打开Lighting界面(Window > Lighting)选择Object标签页
  • 点击齿轮,选择Paste Component Values,刚刚调好的设置就会更新到全部选中的HouseBig02对象上


如上图所示,Copy Component是复制设置到另外一个场景较为简便的方法,当必须调整场景中好几百个对象的设置时,这样的操作更能节省时间,将时间花在其它优化上。

UV展开设置应该考虑实例(pre-instance)还是预制件(pre-Prefab)呢? 可以将UV展开设置保存在预制件(Prefab)里,只要将这些带有Static Mesh Renderer组件的对象存成预制件即可,或是直接将设置赋给场景中的对象。如果将UV展开设置赋给一个由Prefab实例化(Instance)的对象,那将会覆盖掉原本在Prefab中的设置。这是一种在某些情况下能提供设置场景光照的实用做法。

为预制件设置一个默认的UV展开和光照设置通常很有用,如果Prefab需要经常被实例化,这么做就能预先设置对象的UV展开,以节省在场景中进行重复设置的时间。

另一方面,这种做法适合一些特定场景的光照设置,在这种场景中的物体会受周遭环境不同程度的影响。例如,在离玩家近的区域内看到的预制件可以设置更高的品质,反之,如果物体离得很远,那就不必使用同样的设置,这里应该降低光照贴图品质,并降低光照图的数量。

可以配置一个符合大部分情况的设置,然后在需要的情况下去覆盖这些设置。而本文中,我们是针对每个实例对象进行设定。

整个场景的UV展开
现在是时候学以致用了,就以示例场景为例。在显著改善了预计算时间后,设置场景应该用不了太久。操作步骤如下:
  • 打开LightingTutorialStart场景
  • 从层级视图(Hierarchy)中找到Environment > Structures最上方的对象
  • 将该对象复制到一个空场景

到这里应该利用所学内容,尝试各种不同的值,善用UV Charts绘制模式和Charting模式做对比,找到平衡较好的设置。

如果不知道如何设置,LightingTutorialOptimal这个示例场景可以作为一个很好的参考,虽然大多数情况下使用预设值就可以得到很好的效果。
  • 尝试使用不同的值来找出光照图数量最低和失真度最小的平衡点
  • 复制设置,回到LightTutorialStart场景将设置赋给所有相同对象。 选择多个对象可以一次粘贴所有设置
  • 对层级视图(Hierarchy)里Structures目录下所有单独命名的对象重复此步骤
一旦所有在Structures目录下的物件都有了优化设置,就可以转到Rocks目录下处理剩下的静态对象了。以这种方式手动循环设置好场景中所有的静态对象,可以确保更好的迭代流程和更佳的执行效能。

集群和微调光照贴图参数

集群的概念
在学习之前,我们先来了解一下集群的定义与用法,以及它对全局光照的影响,以便后续更好地理解。

修改集群(Cluster)是减少Unity预计算流程所需执行的工作数量的好方法。与此同时,降低集群数量也能提高运行时的性能。当采用PRGI来计算场景光照时,Unity对生成静态场景立体像素化结构的计算进行简化,这些体素叫做集群。集群实际上是映射到用于照明的静态几何体表面的小贴片,以一种层级关联的结构存储,用来预处理漫反射全局光照的复杂辐射度计算。虽然集群和光照图的映射方式很像,但两者是各自独立的。

 

场景视图的Clustering绘制模式下可以查看预计算实时GI所计算出的集群大小。集群会对其被映射到的静态物体的反射率(Albedo)进行采样,然后在预计算的Light Transport阶段计算这些集群间的关联,以便让光照在整个集群网络之间进行传送。Unity生成静态场景近似的低分辨率环境,以便简化在运行时需要更新的光照数据量,从而在保证帧率的情况下传送全局光照。

 
集群X的光照值与附近的集群值之间的关联(本图由Geomerics提供)

一旦预计算完成后,就可以修改环境光(Skybox)及光照位置、强度和颜色,不需要再重新进行预计算。更新后的光照会通过集群网络,将场景的反射率和自发光材质一并考虑进来以计算最终的光照反射结果。

第一次反弹之后的光照结果会被应用到集群里,在每次迭代后集群会被采样到相应的光照贴图纹理,最后供场景中的Shader使用。

由于这个过程在CPU上异步执行,每次更新全局光照所花的时间取决于可用的工作线程。如果需要,可以通过Lighting视图的“CPU Usage”来设定工作线程的数量。 



场景视图的Lit Clustering绘制模式下显示出当光线通过集群网络反弹到集群中的样子。可以通过场景视图的Clustering或Lit Clustering绘制模式来查看集群的分布,在Clustering模式下,场景会覆盖一层由许多彩色方块组成的诊断图,每个方块大小表示映射到静态几何体的集群大小,每种颜色代表场景中不同的集群;Lit Clustering模式也类似,它显示当光照通过集群网络反弹到集群并将光照反射结果反映到集群里的样子。

降低集群的数量在很大程度上决定了更新的速度,也因此决定了Unity预计算实时GI在目标平台的最终效果。我们发现,减少集群数量将会加速预计算光照,而且就结果来看,设置场景光照的迭代速度也会提升,所以减少集群数量是十分有意义的。

光照贴图参数
Unity通过光照贴图参数(Lightmap Parameters)提供了许多高级的光照贴图控制方法,这些设置被存储在一个光照贴图参数(Lightmap Parameters)资源里,以便不同场景或团队协作环境下可以通过版本控制共享光照设置。您可以通过以下三种方法建立光照贴图参数(Lightmap Parameters)资源:
方法一:打开Project视图,从Project视图左上方的Create下拉列表中点击Create > Lightmap Parameters。
方法二:在Project视图中按右键弹出菜单,选择Create > Lightmap Parameters。
方法三:依次点击编辑器顶部菜单(Asset > Create > Lightmap Parameters)。

 
光照贴图参数可以针对烘焙GI或预计算实时GI进行高级设置

接下来可以将创建完成的光照贴图参数集指定给Static MeshRenderer组件,具体操作步骤如下:
从Hierarchy视图选择要指定光照贴图参数资源的对象,请注意,该对象必须带有Mesh Renderer组件且标记为静态。
打开Lighting界面(Window > Lighting)并选择Object标签页。
从Advance Parameters下拉列表赋值光照贴图参数集给组件,点击右边的”Edit"按钮可以快速开始编辑光照贴图参数。


如上图所示,我们可以从Lighting界面Object标签页中Advanced Parameters字段的下拉列表指定光照贴图参数集。同一个光照参数集可以赋值给多个不同的组件,只需从Hierarchy视图一次选定所有需要赋值的Object,执行上面说明的步骤将资源赋值到Advanced Parameters字段即可。

那么光照贴图参数集应该赋值给实例(per-Instance)还是预制件(per-Prefab)?与UV展开设置类似,光照贴图参数集可以赋值给预制件(Prefab)或场景中的独立对象,一旦光照贴图参数集被赋给场景中的预制件实例就会覆盖掉预制件原有的设置。这个方法的好处是可以将正常情况下会用到的光照设置放在预制件中,然后依照不同需求修改并覆盖场景里实例对象上的光照贴图参数设置。

下面我们以实例对象来示范一下如何应用光照贴图参数集,这样就可以根据对象所在的不同环境来选择不同的光照贴图参数集。

默认参数集
配置场景通用的默认光照贴图参数集将被赋给场景中所有带有Mesh Renderer组件但还未指定Lightmap Parameters的对象。场景中任意新建的带有Mesh Renderer组件的对象都将被赋予该默认参数集,这样可以省下手动赋值的时间。具体操作步骤如下:
打开Lighting界面(Window > Lighting)并选择Object标签页。
找到General GI区域下的Default Parameter,选择刚才创建的光照贴图参数集。

虽然新建的对象会应用这个默认设置,但仍可以从Advanced Parameter参数中单独设置。

 
默认设置会应用到所有新建的带有Static Mesh Render组件的对象

Unity提供了几组默认的光照贴图参数集:

Default - HighResolution
Default - Medium
Default - LowResolution
Default - VeryLowResolution

这些不同的默认参数集中配置了会影响到选定对象整体光照开销的设置,包括光照贴图分辨率、全局光照参数、集群分辨率以及其他一些高级设置选项。这些设置对平衡整个场景的光照性能来说至关重要。

光照贴图集参数说明
Unity的默认参数集已经覆盖了大多数常见用例,并且对大多数光照场景都适用。但更好地掌握预计算实时GI系统可以帮助我们创建自己的参数集,进行更多控制。Unity手册上有提供关于光照贴图参数集所有默认设置的说明,下面会重点说明几个对光照优化最有用的设置。

分辨率
分辨率(Resolution)的值决定了使用该光照贴图参数集的对象所需的光照贴图分辨率,这个值会与Lighting界面里场景全局的分辨率相乘。例如,如果场景实时分辨率设为2,这里的Resolution属性设为0.5,那所有带有这个参数集的对象都会采用1 texel/unit来计算光照贴图纹理。

由于PRGI(预计算实时GI)只会呈现场景里的漫反射(Diffuse)和间接照明,所以我们不需要用到像传统烘焙贴图那么高(20-30)的采样值,PRGI通常设定为2-3就已经很好了。大多数情况下,尤其是户外场景如地形等,甚至可以缩小很多倍。如下图所示的光照贴图分辨率就是0.1 texel/unit,已经可以提供足够多的细节。 

分辨率很大的光照贴图所产生的影子斑点可以通过调高Irradiance Budget这个参数来缓解。需要注意,使用高分辨率光照贴图时,可能会出现使用低分辨率时不能发现的缺陷。这些阴影在最终的光照贴图纹理中可能看起来像是斑点或污迹。如果出现这种情况,可以试着调高Irradiance Budget参数进行改善。

集群分辨率
集群分辨率用来决定1个像素里能有多少集群数量。假如这个值设为1,代表光照图里面每个像素都会有一个集群,0.5代表一个像素会有2个集群,换句话说集群会是一个光照图的两倍大小。

进一步来说,可以想像如果将场景的分辨率设为1,并建立一个1x1x1的立方,然后将这个光照参数集赋给立方,那么立方的每一面都会有1个集群。如果将分辨率改为2,结果立方每一边的集群数量就会变为2倍,每面的集群会变成4个。


上图表示实时分辨率设为1的1x1x1方块,光照参数设定集群分辨率(Cluster Resolution)为1 Cluster/texel和光照贴图分辨率为2 texel/unit时,方块每一面都会有4个集群。在大多数的情况下,集群分辨率(Cluster Resolution)只需要设为低于光照贴图像素(lightmap texels)大小即可,例如Unity默认的光照贴图设置Default - HighResolution,就将Cluster Resolution设为0.6。

如本文第一节所述,一个场景中集群太多的话会加长预计算时间,还会影响到运行时的场景全局光照。因此必须要确保增加的集群数量可以显著提高光照贴图的品质,如果减少集群数量对光照贴图并没有太大影响,那就尽可能地减少集群数量。

让光照贴图分辨率和集群分辨率成比例,这样就可以和场景整体的实时分辨率建立一个相关关系。我们可以将Lighting界面中定义的分辨率作为场景整体的高级分辨率,然后针对个别对象或对象组对各自的光照贴图参数集进行微调。Unity采用这种分层法可以为开发者提供更方便的全局控制,不然对每个对象都手动设置光照贴图参数的值会很麻烦。

Irradiance Budget
本文开始的时候有介绍过Unity如何利用集群来生成近似的场景静态几何体以计算预计算实时GI。在预计算过程中已计算好集群之间的关系,以便光照可以在分层网络内快速传递。

本质上,光照贴图的像素值取决于从该像素的位置能“看到”场景集群的数量,这样可以很快计算出光照在集群之间的反弹以生成全局光照的效果,这些集群就能在最终渲染之前被适当进行采样。

Irradiance Budget决定每个光照贴图像素采样集群网格时所使用的内存量,这会决定光照结果的精度。Irradiance Budget数值越低表示每个贴图像素在记录场景信息时使用的内存越少。减少内存的同时也降低了运行时的CPU开销,但代价就是损失光照精确度,数值越低光照结果会越模糊。反之,Irradiance Budget数值越高则GI结果越精确,但相应的内存用量和CPU消耗都会提升。

如果预计算实时GI在运行时更新较慢或者有延迟,可能减小Irradiance Budget值会得到改善,这个设置比较适用于不太注意到的对象,如很大、模糊或遥远的几何体。

Irradiance Quality 
当计算PRGI时,每个光照贴图像素会开始对场景投出射线,以收集附近集群的可视信息。然后计算出贴图像素可以看到的每个集群的百分比,这个值用来定义光照贴图里每个像素从集群所分到的可视数据,而Irradiance Quality就是用来指定每个像素能对场景投射的射线数量。

如果场景里的物体和周围环境光照不合的情况下可以适当加大这个值,有时本该较暗的光照结果却意想不到得亮,有可能是因为投射到场景的射线不足或被遮挡,导致漏计算了集群信息。同样本该亮的地方如果射线没有被检查到,可能会造成过暗的结果。

增加射线的投射量就能解决类似的问题,其代价是增加预计算的时间。想要优化这个时间,就应该找出最适合的值来达到理想的照明效果。请注意,这个值不会影响到运行时的性能。

Backface Tolerance 

当射线从光照贴图像素投射出时,从场景的集群搜集光照信息时偶尔会打到几何的背面,当计算全局光照时仅关注投射到物体表面的光照,从背面来的光照信息通常都会被忽视,这些从背面来的光照信息会破坏光照结果,因此调整这个值能防止出现这类情况。


这里的地板上的阴影就是Unity在计算辐射度时,射线投射到物体无效的背面所生成的,增加Backface Tolerance的值能改善这个问题。Backface Tolerance指定必须从物体前方来的光照百分比,以便正面的贴图像素被判定为有效。假如贴图像素未通过测试,Unity会采用邻近的像素值来估算正确的光照结果。

调整Backface Tolerance并不会影响PRGI的性能,也不会对预计算时长有太大影响。如果调整Irradiance Budget也无法解决场景贴图太亮或太暗的问题时,Backface Tolerance会是很有用的解决办法。

Modelling Tolerance
有时在场景静态物体中存在小间隙的区域会产生黑色的光晕或预期外的残像,这种情况会出现在旁边有相反几何体的区域,像是地板上的组件,如下图所示。

 
物品的黑暗边缘通常可以通过加大Modelling Tolerance来改善

通常在这种情况下,物体之间存在较小的缝隙,因为没有太多光线可以进入,导致产生非常暗的光照图。当光照贴图分辨率较低时,这些黑暗像素的大小可能会导致它们超出遮挡物的边界并产生不协调的黑色块。

Modelling Tolerance用于忽略比Threshold值还小的细节,提高Modelling Tolerance值可以确保Unity在计算GI时忽略掉非常靠近其它物体表面的几何面,Modelling Tolerance值越高代表预计算的Light Transport阶段将忽视这些小于光照贴图纹理像素百分比的间隙,调低Modelling Tolerance可以确保小物体不被GI计算遗漏。

Modelling Tolerance不会影响PRGI的优化,对预计算时间和运行时性能也没什么负面影响,它能用在一些除错流程上,解决连调整Irradiance Budget或Irradiance Quality都无法改善的残影问题。

开始建立光照贴图参数资源
您现在已经了解光照贴图参数能做什么了,接下来一起看一下建立光照贴图资源、微调光照贴图资源参数和为其他物体指定光照贴图参数集的方法。

创建资源
下面我们可以开始尝试创建这些资源,具体步骤如下:
打开LightingTutorialStart场景。
从Project视图中找到Assets > LightmapParameters目录。
在Project视图上方依次点击Create > Lightmap Parameters新建一个光照贴图参数集。
将新建的Lightmap Parameters命名为"TutorialTerrainLow"。


本例中我们考虑上图中的山型物体,从相机镜头看起来大且远,比较适合设置光照贴图参数集而非使用场景默认的设置。

下面将刚才新建的光照贴图参数集指定给该物体:
在场景视图选择最接近主场景的两座山,这些山都已经被命为"MountainPeak"。注意,我们将忽略最远的山。
在检视面板(Inspector)中确保将两个物体都设为静态。
打开Lighting视图(Window > Lighting)并选择Scene标签页。
确保勾选最下方的Auto。
选择Object标签页。
在Advanced Parameters下拉列表中选择刚创建的TutorialTerrainLow。
点击右边的Edit按钮,准备开始调整设置。

 
默认的光照贴图参数集设置

参数调整 — Resolution
第一个参数是分辨率(Resolution),降低这个值可以减少预计算时长同时提升运行时性能,所以应该在不影响画质的前提下采用较低的分辨率。对于离相机镜头很近的物体可以设置比较高的分辨率,但这里两座山距离镜头都非常远,因此本例中将该值设低一点更合适。 这些大物体表面材质的反射率相当一致,从镜头的角度来看,接收到光的颜色变化也不大,由于不需要获取更多光照细节,所以降低这个值并不会影响光照结果的品质。

 
像山或地形这样的大场景,光照贴图分辨率采用默认值反而很浪费

 
使用较低的分辨率会稍稍牺牲一点品质,但可以大大节省预计算时长并提高运行时性能

尝试缓慢调低TutorialTerrainLow光照贴图参数集里面的Resolution参数并观察结果的变化。

请记住,这里的分辨率和Lighting视图中的场景分辨率是会相乘的,目前场景分辨率是1,代表如果这里分辨率小于1,输出的光照贴图每个单元也会小于1纹理像素(texel)。两座山和其它物体距离非常远,所以它们不可能接受很细节或是频率很高的光照,也就是说如果采用较低的分辨率也不会牺牲太多品质。我们发现在示例场景中使用0.05就足以获取。
在Project视图中选中TutorialTerrainLow资源。
在检视面板中将Resolution设为0.05。

参数调整 — Cluster Resolution
然后来看看集群分辨率(Cluster Resolution)的值,这个值也是一个用于和光照贴图像素数量相乘来计算集群的乘法器,当数值为1时集群数量就等于光照贴图像素数量,因此低于1会比较好。集群本质上是为了计算PRGI所生成的类似静态场景的模拟体,所以只有在数量不足以反映物体的光照反射时才需要加大这个值。

这两座山的集群分辨率与光照贴图分辨率类似,也可以设为很低的值,如果将其设为场景默认的分辨率来设置集群数量,就表示可能会需要几百个小场景的集群量来覆盖这两座山。 


对于很大且离相机较远的物体,集群分辨率可以设很低。
将场景视图左上方的绘制模式改为Clustering
从Project视图选择TutorialTerrainLow光照贴图参数集
从检视面板中缓慢调低Cluster Resolution的值,会发现彩色色块慢慢变大,数量则会慢慢变少

另外,在Lit Clustering绘制模式下可以评估设置的分辨率是否足以采样场景的光照细节,具体方法如下:
将场景视图左上方的绘制模式改为Lit Clustering
从Project视图选择TutorialTerrainLow光照贴图参数集
从检视面板中缓慢调低Cluster Resolution的值,观察场景的变化。

调整Cluster Resolution的值时,可以看看是否能接受更低的集群数值,如果继续调高对画质已经没有明显改善了,那就应该使用更少的集群数。请记住,集群数量对于预计算时长和GI的运行时性能有很大的影响,本例中使用0.4较为适合:
从Project视图选择TutorialTerrainLow光照贴图参数集
将Cluster Resolution设为0.4

 
Lit Clustering绘制模式可以用来评估场景中的集群数量是否足够

大多数情况下,仅需降低光照和集群的分辨率就可以提升很大的性能,但还可以利用Irradiance Budget和Irradiance Quality做进一步改善。

参数调整 — Irradiance Budget 
首先看看Irradiance Budget,如之前所说,这个值控制贴图像素在计算时所占用的内存,为了运行时性能,我们应该使用最低的值以免带来负面影响。如果预计算的结果模糊不清,可以试着调大这个值,对于场景中对比度较高却没有被精确表现出来的区域是很有用的。

 
Irradiance场景绘制模式可以用来评估调整Irradiance Budget或Quality的值


两座山接收光照的频率较低,也没有任何高对比的光照处理,这表示在不影响品质的情况下,和可以将默认值128调到更低:
将场景视图左上方的绘制模式改为Irradiance
从Project视图选择TutorialTerrainLow光照贴图参数集
从检视面板里把Irradiance Budget的值慢慢降低
当减少这里的值时,两座山上的照明会变得柔和一点

经过一些实验之后,本例中将Irradiance Budget设为64最合适:

从Project视图中选择TutorialTerrainLow光照贴图参数集
在检视面板中将Irradiance Budget的值设为64

参数调整 — Irradiance Quality
现在来看看另外一个Irradiance Quality,如之前所说,Irradiance Quality影响计算光照像素时从集群所投射出来的射线数量,不像Irradiance Budget那样只会影响到预计算时间,这个设置会影响到运行时性能。

本例中的两座山是非常大的物体,这表示它们不太容易错过邻近的集群,因此可以通过降低Irradiance Quality值来提升预计算的性能:
将场景视图左上方的绘制模式改为Irradiance
从Project视图中选择TutorialTerrainLow光照贴图参数集
在检视面板中缓慢调低Irradiance Quality的值
随着值越小,生成的光照贴图会变得越来越模糊

本例中2048是一个既能保持想要的品质又能提升计算性能的平衡点,虽然精确度会稍有损失,但整体来看是利大于弊的:
从Project视图中选择TutorialTerrainLow光照贴图参数集
在检视面板中将Irradiance Quality的值设为2048

为其它物体指定光照参数集
下面可以为LightingTutorialStart场景中其它的静态物体创建并指定光照参数集。之前讨论过一种比较好的做法,就是确定某种分类或模式来组织场景中的物体。基于物体的特征(例如大小或是与镜头的距离)将其分组,以便更容易地为物体指定光照贴图参数集。

在处理Environment组件下方其余的子物体时,请尽量让能够共享光照贴图参数集的物体分为同一类,例如:物体是否会很接近可玩区域及相机镜头。这种情况下如果为该物体提高光照贴图分辨率对于结果会有明显改善,如果是很远的物体就不需要设置相同的值,如果物体材质的反射率没有太大的区别,也没有必要设置高分辨率。物体分组不需要太细,对于大多数情况下,几组差异性大的设置就够了,分太细也不便于管理,可能每个细节都要针对物体独立修改。

在已完成的场景LightingTutorialOptimal中,我们分组依据如下:

RockLow:距离镜头较远的光滑石头,因为距离远所以光照贴图分辨率低。
RockMedium:光滑石头,更接进可玩区域,所以需要设置高一点的光照贴图分辨率。
StructuresHigh:需要高分辨率呈现的重要建筑物,同时也需要更高的Irradiance Budget和Quality设置。
StructuresLow:远距离的建筑物,只需要低分辨率设置。
TerrainLow:大型网格且距离相机远,比如山,不需要高分辨率
TerrainPlayable:比较靠近镜头的大型地形,需要一个光照贴图分辨率来平衡,降低大地形的集群数量也较为合理
TerrainVeryLow:非常遥远且部分被遮挡的地形,分辨率和集群数量均很低,Irradiance Budget和Quality也都很低

除了这些自定义参数之外,在已完成的示例场景LightingTutorialOptimal中,还包含一些使用默认光照参数的结构,这里用的是默认设置Default-Medium。使用场景中大多数静态物体默认的参数集可以减少很多手动的工作,将常用值指定为默认参数设置会很有帮助,对于本例,Default-Medium和默认为1像素的分辨率搭配使用较为合适。


Irradiance绘制模式用来评估场景照明非常有用,因为它只会显示对PRGI有贡献的静态物体光照结果。

当决策场景光照的赋值与配置时,可以用LightingTutorialOptimal作为一个评估基础,将光照贴图参数集指定给场景的物体不会花费太久的时间。记住,当设置这些组件时,要不断的考虑调高参数值所带来的好处有多少,如果调低参数带来的负面影响不大,那就要优先选择较低的参数值。

总结
优化场景光照的用意与游戏开发的许多方面一样,就是希望在性能和视觉效果之间找到一个完美的平衡,在大多数情况下,牺牲少量的光照精确度来加快预计算时间以及改善运行时性能是很值得的。处理好整个场景的预计算之后就不需要额外计算不同时间的光照,这种效果使用传统的烘焙光照是不可能达成的。

恭喜您已经阅读完所有预计算实时GI有关内容!经过这几篇文章的学习,您已经了解到如下内容:
如何评估场景并决定适用于预计算实时GI的光照贴图分辨率。
预计算光照过程中最耗性能的元素之一:光照图(Chart)数量,并学习减少光照图数量的方式。
如何为小物体设置光照探针。
如何调整Unity的预计算参数,在UV展开过程中减少光照图的数量。
了解集群的定义与用法,以及它对全局光照的影响(本文)。
学习如何微调影响场景物体的光照贴图分辨率及精确度,对视觉效果影响较小的物体可以适当降低其预计算的消耗(本文)。

将所有这些技术结合再一起就能用最少的预计算时间高效实现产品级别的光照效果,再结合Unity快速迭代实时将光照更新到GI以及实时改变光照反射的能力,Unity引擎是大多数应用实现预计算实时GI解决方案的不二之选

这篇关于Unity 预计算实时GI的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/331947

相关文章

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Unity Post Process Unity后处理学习日志

Unity Post Process Unity后处理学习日志 在现代游戏开发中,后处理(Post Processing)技术已经成为提升游戏画面质量的关键工具。Unity的后处理栈(Post Processing Stack)是一个强大的插件,它允许开发者为游戏场景添加各种视觉效果,如景深、色彩校正、辉光、模糊等。这些效果不仅能够增强游戏的视觉吸引力,还能帮助传达特定的情感和氛围。 文档

三.海量数据实时分析-FlinkCDC实现Mysql数据同步到Doris

FlinkCDC 同步Mysql到Doris 参考:https://nightlies.apache.org/flink/flink-cdc-docs-release-3.0/zh/docs/get-started/quickstart/mysql-to-doris/ 1.安装Flink 下载 Flink 1.18.0,下载后把压缩包上传到服务器,使用tar -zxvf flink-xxx-

Unity协程搭配队列开发Tips弹窗模块

概述 在Unity游戏开发过程中,提示系统是提升用户体验的重要组成部分。一个设计良好的提示窗口不仅能及时传达信息给玩家,还应当做到不干扰游戏流程。本文将探讨如何使用Unity的协程(Coroutine)配合队列(Queue)数据结构来构建一个高效且可扩展的Tips弹窗模块。 技术模块介绍 1. Unity协程(Coroutines) 协程是Unity中的一种特殊函数类型,允许异步操作的实现

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光 一,前言二,资源包内容三,免费获取资源包 一,前言 在创意的世界里,每一个细节都能决定一个项目的独特魅力。今天,要向大家介绍一款令人惊艳的粒子效果包 ——Super Confetti FX。 二,资源包内容 💥充满活力与动态,是 Super Confetti FX 最显著的标签。它宛如一位

Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(4)

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(3)-CSDN博客  这节就是真正的存储数据了   理清一下思路: 1.存储路径并检查 //2进制文件类存储private static string Data_Binary_Pa

Unity Adressables 使用说明(一)概述

使用 Adressables 组织管理 Asset Addressables 包基于 Unity 的 AssetBundles 系统,并提供了一个用户界面来管理您的 AssetBundles。当您使一个资源可寻址(Addressable)时,您可以使用该资源的地址从任何地方加载它。无论资源是在本地应用程序中可用还是存储在远程内容分发网络上,Addressable 系统都会定位并返回该资源。 您

Unity Adressables 使用说明(六)加载(Load) Addressable Assets

【概述】Load Addressable Assets Addressables类提供了加载 Addressable assets 的方法。你可以一次加载一个资源或批量加载资源。为了识别要加载的资源,你需要向加载方法传递一个键或键列表。键可以是以下对象之一: Address:包含你分配给资源的地址的字符串。Label:包含分配给一个或多个资源的标签的字符串。AssetReference Obj

在Unity环境中使用UTF-8编码

为什么要讨论这个问题         为了避免乱码和更好的跨平台         我刚开始开发时是使用VS开发,Unity自身默认使用UTF-8 without BOM格式,但是在Unity中创建一个脚本,使用VS打开,VS自身默认使用GB2312(它应该是对应了你电脑的window版本默认选取了国标编码,或者是因为一些其他的原因)读取脚本,默认是看不到在VS中的编码格式,下面我介绍一种简单快

Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(3)

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(2) (*****生成数据结构类的方式特别有趣****)-CSDN博客 做完了数据结构类,该做一个存储类了,也就是生成一个字典类(只是声明)  实现和上一节的数据结构类的方式大同小异,所