间接光没有办法实现实时计算,所以需要一套GI系统去处理间接光。
- GI系统主要解决的是间接光漫反射的实现,实现的载体是LightMap、Light Probe、Refletion Probe。
- 需要一个后台程序(离线渲染器)来实现离线渲染。
- 可以根据不同的物体(静态、动态),不同的光源类型(平衡光,点光源)采用不同的方式去实现渲染(举例,静态物体烘焙完成后,不会受到实时光的影响)
unity的GI系统,本质上就是在后台开启一个离线渲染程序执行GI计算。最早的unity的离线渲染器也是调用的第三方的,所以,如果实力允许,我们也可以独立实现相应的功能。
unity的离线渲染器更新:Beast->Enlighten->Enlighten + Progressive Lightmapper
使用
-
首先,需要标记Contribute GI让物体参与到烘焙或者预计算中。
设置了Contribute GI(贡献全局光照)以后,烘焙的时候,当前模型会参与到烘焙和预计算当中。右上角和在Renderer上面设置的效果是一样的,你还可以设置Receive Global Illumination (接收全局照明)选择是从LightMap还是Light Probe。 -
模型的材质需要使用包含Meta的pass,也就是Tags里面的LightMode = Meta。下面为unity内置的Lit携带的meta pass
// This pass it not used during regular rendering, only for lightmap baking.
Pass
{
Name "Meta"
Tags { "LightMode" = "Meta" }
Cull Off
HLSLPROGRAM
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5
#pragma vertex UniversalVertexMeta
#pragma fragment UniversalFragmentMetaLit
#pragma shader_feature EDITOR_VISUALIZATION
#pragma shader_feature_local_fragment _SPECULAR_SETUP
#pragma shader_feature_local_fragment _EMISSION
#pragma shader_feature_local_fragment _METALLICSPECGLOSSMAP
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED
#pragma shader_feature_local_fragment _SPECGLOSSMAP
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitMetaPass.hlsl"
ENDHLSL
}
如果没有相关pass,也可以实现渲染,但是Albedo默认中灰色,无自发光Emission。
3. 需要有光源设置为Baked(已烘焙) 或者 Mixed(混合模式),需要设置开启了烘焙系统后才可以选择。
接下来,可以在光照的配置项下面设置相关配置,在参与烘焙的物体上面设置,也会影响最终烘焙结果。
Enlighten
Enlighten是基于辐射度实现的一种光线追踪。
原理:
- 辐射度算法将场景中的资产划分为多个面片(Cluster/Patch),面片不同于模型创建的三角面。以面片为单位进行计算,最终结果是计算出每个面片的辐射度(Radiance)
- 面片辐射度的计算公式 面片辐射度 = 面片自身光照发射率 + 其它面片对当前面片的入射光反射率的总和。
B对A面片的入射光反射率 = B面片的辐射度 * AB面片的形状系数(form factors,形状系数可以理解成是两个面片之间的可见度,取决于面片之间的距离和相对的朝向。) - B面片的辐射度也需要上面的公式去计算,所以要递归计算形状系数去求出反射率,1次递归是面片数的平方,两次递归是面片数的立方的计算量。
- 需要将每个面片对其它面片辐射度存储下来。(如果用的是Dynamic LightMap,预计算会计算到这一步)
- 最后就是计算出当年面片的间接光漫反射颜色了,将直接光注入到每个面片,得到每个面片的直接光颜色,计算一个面片的间接光漫反射颜色就需要去获取到能影响它的其它面片对其的影响颜色,最后计算出来间接光漫反射。
- 如果是动态GI(也就是会根据动态光照进行动态改变)其实计算到第四步就完事了,后面的最终间接光漫反射颜色是实时计算得出,生成Dynamic LightMap和Light Probe。如果烘焙GI,那么unity会使用离线器将后面需要实时计算的内容直接烘焙成Light Map和Light Probe.
- 注意,两种烘焙方式的预先条件都是一致的(除了光源,动态GI的目的就是为了能够生成光照的动态GI),所以使用前一定要确定条件是否设置成功。
执行逻辑
- Create Geometry 根据设置的静态几何体,重建场景几何体。
- Layout Systems 将场景几何体划分多个可以并行计算的System
- Create System 创建执行的Systems
上图为创建好的Systems,能看到分成了很多部分进行并行计算,节约时间。
可以点击此处查看,有些版本的unity是无法查看的注意。也有可能是烘焙完成以后,切换场景就会被清除掉,毕竟后续也不需要查看。 - Create Atlas 创建Light Map 图集
- Clustering 每个System会分割成离散的Cluster,Enlighten将世界看做由很多Cluster组成的。CLuster是计算光照的基本单位。
Cluster可以影响烘焙的质量以及速度,精度越高,分块面积越小,
- Visibility 预计算Cluster之间的可见性,From Factor
- Light Transport
- Tetrahedralize Probes 四面体化Light Probes
- Create ProbeSet 创建Light Probes
- 后面还会烘焙一个全局的环境漫反射和环境镜面反射。
在场景里面,我们也可以直接看到烘焙好的Light Map,如果你转动主光源,会发现LightMap也会实时更新。
在场景里,动态GI只对实时光产生作用,你需要将光的属性设置为Realtime,自发光的属性也要设置为Realtime。
图上为unity内置的lit携带的设置(2021版本)
我们也可以在Contributors/Receivers里面查看哪些贡献烘焙光照,以及从哪里获取环境漫反射。
上图显示什么颜色使用和接收的方式。
在场景里面,可以选择Clustering来查看分块情况。
我们看到只开启实时烘焙光照以后,下面的设置项能够影响它的就那几项,Indirect Resolution 就是设置每个单位有几块Clustering的。还有Lightmap Parameters 也会影响它,现在使用的是默认的选项。
它们会相乘得到最终在场景中使用的值,比如默认的是0.5 我们设置也是0.5,那么一个单位平面中的Clustering的面是0.25.
如果动态GI和静态GI都开启的情况下,我们可以选择天光使用哪种方式去烘焙。默认就是动态GI去烘焙,也就是实时烘焙。
Enlighten 烘焙GI
我们也可以开启静态烘焙,使用Enlighten进行静态烘焙
上面的两种是其它的烘焙方式,其实是采用的路径追踪的方式去实现的。开启后是以下的样子,下面就是Enlighten烘焙GI的控制项。
执行逻辑
Enlighten的静态烘焙GI相对于动态GI,还会额外的制作一些流程。
- Atlassing 整理LightMap 图集
- Create Baked Systems 创建烘焙系统,开始第一阶段的烘焙。
- Baking Resources 创建烘焙过程相关的临时资源,比如各种贴图的创建。
- Bake AO 烘焙AO
如果选择了烘焙AO,生成的LightMap将携带AO信息。 - Create Input Lighting 注入需要参与烘焙的光源,动态GI是实时计算的不需要考虑,静态烘焙需要将灯光提前注入。
- Bake Visibility 加入光源,更新From Factor
- Bake Direct 根据LightMap Resolution,烘焙直接光贴图
- Albedo and Emissive 调用shader中的MetaPass给场景结合体注入Albedo和Emissive。
- Create Bake Systems 创建烘焙系统,开始第二阶段的烘焙。
- Bake Runtime
- Upsampling Visibility 升采样可见性,提升准确度
- Bake Indirect 烘焙间接光部分
- Final Gather
- Bake ProbesSet 烘焙Light Probes
- Compositing 最终合成
- 后面还会烘焙一个全局的环境漫反射和环境镜面反射。
简单理解:先烘焙面片之间的形状系数,然后烘焙AO,直接光,MetaPass上色,间接光,最后合并到贴图,最后烘焙Light Probes(这一步不属于Enlighten的范畴,属于unity直接实现,可以通过生成照明右侧的下拉,找到Bake Reflection Probes)。
动态GI和静态烘焙GI生成的两套LightMap,也需要两套LightMapUV去对应。静态烘焙GI会生成贴图,然后根据uv和TillingOffset去场景上获取对应位置储存的颜色。
Progressive Lightmapper
这是后来又增加一套新的烘焙系统,主要解决Enlighten烘焙速度慢的问题。
光照贴图烘焙器的前两个都是用的这套新系统,GPU是使用显卡运算的,速度快,精度不高(因为显卡不适合做逻辑运算),默认使用的是CPU,速度慢一些,但是计算的结果比较精确,所以我们使用的结果都是先使用GPU计算。
原理
在路径追踪算法中,我们要计算某个点的GI,就要收集它上半球上所有可能方向的所有光线,那就需要发射无数根光线去拾取,所以我们需要进行积分,但是很难实现。
当一个积分很难通过解析的方式得到答案时候,可以通过蒙特卡洛的方式近似得到积分的结果。
蒙特卡洛积分方法近似的效果好坏主要取决于采样的样本数量。Samples(平面上的采样点,也可以代表采样用的一根射线)次数越多,获取的结果也就越接近实际结果,相应的也会增加计算时间。
重要性采样,在有限的Samples数量基础上增加准确度。
路径追踪算法在确定好Samples数量以及Bounces(弹射)次数。
根据设定的LightMap Texel(光照贴图像素)的数量,对每个像素进行Path Tracing,烘焙LightMap。
先烘焙直接光照(直接怼直接光源进行采样),烘焙间接光,最后是环境采样。
对LightMap降噪
对Light Probe进行Path Tracing,烘焙Light Probe
配置
现在路径追踪只能用于烘焙GI
渐进更新:配合自动生成可以修改静态模型自动更新。如果不需要自动生成,则此项不需要开启。
多重重要性采样:就是上面提到的重要性采样,有的时候会造成采样的不准确。保持勾选即可。
直接样本:每个LightMap Texel的计算直接光照是发射的Samples数量,默认即可。
间接示例:每个LightMap Texel的计算间接光照时每次弹射时发射的Samples数量。此数值比较影响烘焙时间。一般在户外场景为100足够,某些室内场景对间接光要求较高的,可以提升到256以上。
环境样本:计算环境光所用的Samples总数,Samples的数量分配由LightMap Texel和Light Probe Position的分布情况决定。默认数值是500,如果用了HDR Skybox可以适当提高此数值,来达到更好的效果
光照探测器样本乘数:这个是一个倍数,Light Probe采样时,会通过
反弹次数:间接反弹次数,影响烘焙时间,对于大多数场景2即可,室内场景可以适当提高。
最小反弹次数:在间接光照计算值最小反弹次数,建议2。为了提高烘焙性能,Lightmapper会根据情况减少采样。
最大反弹次数:最大反弹次数,建议2-10之间,输出的时候可以增加高些,调试就低一些。
过滤:对LightMap进行降噪处理,默认是使用高斯模糊。高级是使用A-Trous,降噪的同时最大限度减少模糊。
间接分辨率:Enlighten用的,每个单位多少的Enlighten的面片,也会影响Dynamic LightMap的分辨率,光线追踪用不到。
光照贴图分辨率:每个单位有多少的像素
可以通过场景查看,每个格子代表一个像素。
光照贴图分辨率:每单位有多少光照贴图的像素,也可以对单个物体进行分辨率调节。Lightmapper根据像素进行光线追踪。
光照贴图填充:每个物体在光照贴图上面的像素间隔。
最大光照贴图大小:每个光照贴图的最大尺寸,数值设置推荐合理一些,小了影响合批,大了有带宽问题。一般在1k-2k
Lightmap Compression:光照贴图的压缩设置。光照贴图保存的是HDR数据,注意格式对光照贴图的影响。
无:相当于无损压缩。
低质量:使用双低动态范围 (dLDR) 编码。只需直接将 [0, 2] 范围映射到 [0, 1],即可在移动平台上使用 dLDR 编码。值高于 2 的烘焙光源强度将被钳制。编码值的计算方法是:光照贴图纹理的值乘以 2(如果使用伽马空间),或乘以 4.59482(22.2)(如果使用线性空间)。某些平台将光照贴图存储为 dLDR,这是因为在使用 RGBM 时,这些平台的硬件压缩会产生外观不佳的瑕疵。
法线质量:normal quality 默认的质量 使用RGBM编码,推荐。
高质量:使用
https://docs.unity3d.com/cn/current/Manual/Lightmaps-TechnicalInformation.html
环境光遮蔽:是否开启AO,开启AO会烘焙一张AO贴图,最后在合并到光照贴图上。
最大距离:AO射线检测的最大距离,理论上数值越大,产生的AO会更真实,也更大。有要求10就很好了。
间接贡献:间接光对AO的贡献。
直接贡献:直接光对AO的贡献,默认为0.
定向模式:开启定向模式后,会额外烘焙一张定向贴图,记录每个像素的入射光方向,但是只能记录一个,最重要的那个。后续就是我们可以在渲染shader中获取到入射光朝向,让漫反射体现法向贴图的效果。
可以找到入射光的方向,实时GI和烘焙GI分别有。
L = directionMap.rgb * 2 - 1; //贴图转方向,获取光源朝向
halfLambert = dot(N, L) * 0.5 + 0.5;
bakeGI = lightmap * halfLambert / max(1e-4, directionMap.w); // 权重因子
强化反射率:整体控制后台GI程序中场景物体的Albedo亮度
间接密度:整体控制生成光照贴图和Light Probe的亮度
光照贴图参数:最后一项是光照贴图参数unity分了四个级别,默认中等
光照贴图参数
图上是在模型上面指定使用某种光照贴图参数。
光照贴图参数 Lightmap Parameters 是一个全局的参数,可以在场景中选择使用哪个配置去实现,这样可以实现光照参数一致的情况下,使用光照贴图参数去调整一些影响性能的配置,方便性能分级。
说一下主要的,
分辨率:整体分辨率的调整,可以影响到Lightmap分布和Clustering的大小
群集分辨率:这个也是控制Clustering分块的,光照配置中的值会乘以这个获得最终值。
推开:PushOff 这个值增加会减少阴影的范围,有阴影偏移的功效。
烘焙标记:Baked Tag 如果使用了不一样的光照贴图参数文件或者设置的烘焙标记不一样,将无法合并到一张LightMap中。
背面容差:Backface Tolerance 这个主要是无效像素的问题,比如我们立起一个面片,背面不渲染,那烘焙的时候,底部的面将获取不到背面的颜色,我们可以设置Texel Validity 去检测当前场景有哪些像素无效。
需要我们尽量避免这种情况,如果无法避免,我们可以将此值减少。这个值越大,射线拾取越严苛,相当于一个百分比的值,如果值为1,那么必须发出的射线百分比都要通过测试才能够不会设置为无效像素,如果设置0.5,如果射线检测通过数量不低于一半,那就是有效像素。这个看情况去设置,但是你不设置的话,有的区域会变黑,基本上是这个原因造成的。
我们还有一种方式,就是,在材质上设置一下:
开启双面全局照明,也能够解决这个问题。
还有一些可以直接在MeshRenderer设置影响光照烘焙的。
贡献全局光照:设置了以后,可以参与烘焙,直接烘焙出阴影。
有一些小的物体,比如树叶,草啊什么的,他们烘焙LightMap比较耗,我们可以把接收全局照明修改成Light Probes。
如果你把动态GI打开,还会出现额外的调节参数。动态GI用了一套额外的UV,能调整的地方,也就在meshRenderer身上。
灯的模式
灯的模式分为三种:
- 实时 real-time模式,灯光不会参与烘焙,值参与直接光的实时计算,如果开启了动态GI,动态光照将参与动态光照贴图和光照探针的烘焙中,当前的强度和间接乘数也会影响间接光照的效果。
- 已烘焙:baked模式,直接光,间接光,阴影都会被烘焙的光照贴图和光照探针上,烘焙完成后,光源则不会参与实时计算。
- 混合:Mixed模式,烘焙完成后,依然可以参与到实时计算中,我们可以在shader中拿到光源数据。它具有上面的两种模式的特征,即可以实时,也可以参与烘焙。
照明模式 Lighting Mode
https://docs.unity3d.com/cn/current/Manual/lighting-mode.html 有能力的可以去看官方文档。
照明模式分为三种:
- 间接烘焙:Bake Indirect 模式,只有间接光漫反射会被烘焙到光照贴图和光照探针上,直接光、阴影都需要实时计算,消耗是最高的。
- 减性:Subtractive模式,直接光漫反射、间接光漫反射,阴影都会被烘焙到光照贴图和光照探针上面。你在这种模式下设置了静态参与烘焙,模型将不会参与实时渲染(实时渲染光,参与到阴影贴图,无法对其它物体产生投影,但是可以接受实时阴影),这种模式烘焙就是完全静态,不会受场景变动影响,也不会出现镜面高光。
- shadowMask:只有间接光被烘焙到光照贴图和光照探针上。烘焙时会额外的烘焙一张shadow Mask,用来标记烘焙阴影的区域,超出实时阴影的区域,将会使用烘焙阴影,以达到远处没有阴影的尴尬。
在项目设置,质量里面,可以看到shadowMask可以设置两种模式: - 距离阴影模板,Distance Shadow Mask,超出实时阴影距离,将自动使用shadow Mask阴影绘制。如果模型处于实时渲染的范围,将也会参与实时阴影计算。
- ShadowMask,这种模式静态物体一直使用烘焙阴影,和距离阴影模板有区别的地方。
shadowMask模式需要生成一张额外的贴图,拥有四个通道,所以光照贴图上的一个像素最多可以存储四盏Mix灯的阴影,超出的灯会将阴影烘焙到光照贴图上。
如果一个像素上同时包含四盏以上的mix灯的阴影,将有回退,以上是在Light Overlap下查看,哪几盏灯被认成了Baked模式,无法贡献实时阴影。
缺点:烘焙的内存占用会增加一倍,因为需要一套额外的贴图。
技术选型
直接光
- 实时计算
- 烘焙到光照贴图
- 烘焙到光照探针
直接光阴影
- 实时计算Shadow map
- 烘焙到光照贴图
- 额外烘焙阴影为特殊的LightMap(Shadow Mask)
间接光漫反射
- 烘焙到光照贴图
- 烘焙到光照探针
Debug视图调试
前面的说过了,列一下Baked Global Illumination
Baked Lightmap: 烘焙的光照贴图显示
Directionality: 光的朝向
Shadowmask: 烘焙shadowMask模式下可以查看阴影遮挡情况
Albedo:基础反射颜色
Emissive:烘焙自发光颜色
UV Charts:光照贴图UV映射检查
Texel Validity:像素无效化检测,前面讲了
UV OVerlap:检查光照贴图UV是否重叠的问题
Lightmap Indices:光照贴图的索引查看
Light Overlap:上面说了,查看mix光源被回退到baked的光源情况
#注意事项
- 小而且量大的物体,比如树叶,草,不要参与LightMap烘焙,尽量使用Light Probe
- 如果烘焙LOD模型,每一层都会参与到烘培,简单的方式可以降低其它层级光照贴图面积占用。或者只烘焙第一层,其它层都使用这一层的光照贴图(需要对应uv,而且这种方式还需要额外工具去实现)。
- 缝合线问题,可以处理uv边缘有硬过渡的问题。
https://docs.unity3d.com/cn/current/Manual/Lightmapping-SeamStitching.html - 重启编辑器/电脑可能无法查看Debug 视图,因为这需要GI缓冲文件,需要重新Baked才能看到。