Unity 5.6 光照烘焙系统介绍

一、 基本概念

1. 直接光照、间接光照

直接光照:光源直接照射到物体上,并反射到眼中的光照。

间接光照:光源先照射到其它物体上,并经过一次或多次弹射,最终抵达到观察物体,反射到眼中的光照。

2. 漫反射、镜面反射、环境反射

漫反射:照射到物体表面的光线在各个方向均匀地反射出来,反射强度跟入射角相关,跟观察角度无关。

镜面反射:当入射角和反射角越接近时光照越强,因此不同观察角度反射强度不同,传统的Lightmap无法计算。

环境反射:无法被光线直接照射到的区域,通过光线在物体之间的弹射而被照亮。在不使用全局光照进行计算时,通常简单地指定一个纯色。

3. 光照图、带方向的光照图

光照图(Lightmap):将光照的反射信息预先计算好并存储在纹理上,渲染时直接采样这张纹理模拟实时光照。

带方向的光照图(Directional Lightmap):类似于传统Lightmap,但是额外生成了一张光照图用来保存光源的照射方向以及这个方向的光照强度贡献比例,以便能在使用光照图的同时计算Specular。

示例场景(全静态光照投影)

光照图

带方向的光照图

4. 间接光照图

和传统光照图类似,但只在光照图上存储间接光照的信息,而不存储直接的光照信息。

只有间接光照的渲染效果

间接光照图

5. Light Probes

由于无法对场景中的人物等动态物体像静态物件一样烘焙光照图,计算间接光照。如果场景中仅有静态物体存在间接光照效果,动态物体会显得十分突兀。而Light Probe则是用来为动态物体计算近似的间接光照效果,一般成组地批量使用,分布在场景中。

图中木桶等杂物没有计算间接光照

Light Probe使用球型和谐函数编码记录一定空间区域内的光照信息,占用的数据存储空间很小,只有27个float,并且在Shader中解码的计算开销也很小。但由于没有存储物体表面逐像素的光照数据,因此无法像光照图一样表现出物体的光照细节。

具体的算法可参考文档:http://gdcvault.com/play/1015312/Light-Probe-Interpolation-Using-Tetrahedral

Light Probe Group和红黄色两盏点光,尚未烘焙。

烘焙Light Probes后的动态物体光照效果

6. ShadowMap

通过额外Drawcall绘制物体在光源空间的深度图来实现投影效果,质量较高,但性能开销大。

基于ShadowMap实时计算投影效果

光源空间的深度图

7. ShadowMask

预先计算光线到物体表面的遮挡关系,并存储到纹理中。渲染时通过采样此纹理计算光照投影区域。

ShadowMask图为一张ARGB 32位图,每个通道分别用来记录一个光源是否能直接照射到物体表面。当使用ShadowMask时最多能同时记录同区域内4盏光源的投影信息,如果超过这个值则不会生成ShadowMask,而是将光源作为静态光照直接烘焙到Lightmap上。

ShadowMaskMap

8. 预计算实时全局光照(Precomputed Realtime GI)

通常情况下,如果要实现全局光照的效果,为了性能考虑游戏只会实时计算物体的直接光照,而间接光照则会被预计算并烘焙到光照图中。而在开启预计算实时全局光照时,Unity会预计算静态物体之间的光线弹射传播路径,并使用这些信息在运行时生成低分辨率的间接光照图。这样可以在改变光源位置、方向、颜色时,也能实时计算物体的间接光照效果。

但这样需要在LightingData Asset中保存额外的光照传播路径数据,还需要在运行时生成光照图,产生额外的内存开销和渲染计算量。并且由于用于保存这些信息的光照图分辨率较低,如果直接复用普通光照图的纹理坐标进行采样,会出现很严重的瑕疵,因此必须为其生成单独的纹理坐标。

有关Unity预计算全局光照算法的更详细的说明可参考文章:

https://blogs.unity3d.com/2015/11/05/awesome-realtime-gi-on-desktops-and-consoles/

9. 光照图纹理坐标

模型原始的纹理坐标有可能把不同的面映射到相同的纹理区域,而在采样光照图时,由于不同面的光照结果不同,所以必须要求模型的每一个面都映射到单独的光照图区域。为了解决这个问题,我们可以在建模工具中预先生成好展开的纹理坐标,保存到UV1通道中。也可以使用Unity自带的纹理坐标展开(Unwrapping)算法自动为模型生成光照图纹理坐标。

在生成光照图纹理坐标时,每一块不连续的模型几何面映射区域叫做一个纹理坐标图块(UV Chart)。在采样光照图时,为了避免图块之间因为采样的过滤插值而造成溢色,我们需要在图块之间保持一定的间距。但是这么做会造成光照图空间的浪费。为了避免生成的光照图纹理坐标独立图块数量过多,Unity的纹理坐标生成算法可以自动地将相邻面片的图块拼合在一起。

未合并的纹理图块

根据面片相邻关系合并的纹理图块

而在生成预计算实时全局光照信息的光照图时,系统会自动将纹理坐标的采样边缘对齐到半个像素的位置,因此即便不同的纹理坐标图块之间不保留空白像素,也不会产生溢色问题。

半像素对齐的光照图纹理坐标

但是Enlighten实现的预计算实时全局光照的算法要求光照图的每个图块最小也要有2x2像素的采样Block。如果分割的纹理图块过多,浪费的纹理空间依然会很大。因此Unity还为预计算全局光照图提供了额外的纹理坐标简化算法,可以将指定间距、夹角容差范围内的面片对应的纹理坐标图块进一步合并到一起。

但当开启纹理坐标图块简化时,Enlighten要求拼合后的每个图块的边缘保存分离的光照方向信息,而光照方向信息又是以2x2像素的Block为单位保存的,所以最小纹理坐标图块的尺寸则变成了4x4像素。

二、 Unity新版本变更

在Unity5.6.0以上版本中,官方已放弃了原先的DirectionalLightmap with Specular,如果将灯光设置为Baked的纯静态光照烘焙模式则无法实现任何Specular的光照效果。而新加入的Mixed的混合光照模式则得到了大幅改进。当使用Mixed光照模式时,引擎提供了四种新的光照混合选项,分别为:Baked Indirect、Distance Shadowmask、Shadowmask和Subtractive,这四种选项在混合实时动态光照、静态烘焙光照,实时投影及烘焙投影时采用了不同的处理方法。

三、 逐光源设置

每个光源使用的光照模式通过在Inspector编辑页面中修改光源组件的Mode属性值指定。

每个光源可以设置不同的光照模式。场景中可以同时存在纯实时光照(Realtime)、纯静态烘焙光照(Baked)和动静混合光照(Mixed)。如果同时有多个光源都指定为Baked或Mixed,那么它们需要烘焙的直接或间接光照信息会被混合起来保存在同一张光照图上。

当光源被指定为Mixed模式时,还需要在全局光照设置选项中为所有Mixed光源统一指定具体的混合模式。详细说明可参见章节——混合光照模式。

Intensity用来调整光源的整体强度。

Indirect Multiplier用来单独指定间接光照的强度。

四、 全局光照设置

1. 环境光照设置Environment

Skybox Material: 指定一个用于渲染天空盒的材质。应使用采样Cube Map的特殊Shader。

Sun Source: 指定作为太阳的主光源。将使用该光源的方向来计算光晕特效的位置。

Environment Lighting

环境光源选项

Source:

  • Skybox –使用天空盒作为环境光源。
  • Gradient –使用 Sky、Equator、Ground指定天空、水平线、地面三个颜色作为环境光源,并进行插值过渡。
  • Color – 使用一个纯色作为环境光源。

Ambient Mode:仅当Realtime Global Illumination开启时可选,否则强制烘焙环境光。

  • Realtime –使用球型和谐函数实时计算环境光
  • Baked –将环境光烘焙到光照贴图上。

Environment Reflections

环境反射光源

Source:

  • Skybox – 使用天空盒作为反射光来源。
  • Custom –使用指定的Cube Map作为反射光来源。

Resolution:

环境反射CubeMap的分辨率(覆盖原始天空盒材质纹理尺寸)

Compression:

  • Uncompressed – 不压缩
  • Compressed –压缩
  • Auto –根据纹理格式设置自动选择

Intensity Multiplier:

反射强度控制系数,取值范围从0到1。默认为1,即真实的反射强度。

Bounces:

当场景中存在类似镜子的反射物时,场景可以被反射的最大次数。如设置为1,则环境反射贴图中的镜面则不会被绘制,显示为黑色。

2. 实时光照设置Realtime Lighting

Realtime Global Illumination:

是否开启预计算实时全局光照。

如开启则会烘焙间接光照传递信息贴图,占用额外的内存,并影响渲染性能。

如关闭则将全局光照结果烘焙到光照图中。

3. 混合光照设置Mixed Lighting

Baked Global Illumination:

控制是否烘焙全局光照。如关闭,则Mixed和Baked光源均不会生成光照贴图。

Subtractive

光照:

等同于Unity5.6版之前Non-Directional光照烘焙模式,将直接光照和间接光照都烘焙到光照图上,但不会有镜面反射的高光效果。

投影:

如光源为Mixed模式,在ShadowDistance距离内,动态物体可使用ShadowMap在动态物体上产生投影,但动态物体只在静态物体上产生主光源造成的投影。并且必须通过Realtime Shadow Color指定一个强制的投影遮挡区域间接光照颜色。在ShadowDistance距离外动态物体不产生投影。

静态物体在静态物体上的投影使用Lightmap计算,在动态物体上的投影使用LightProbe计算,均不受ShadowDistance影响。

Baked Indirect

光照:

直接光照实时计算,间接光照烘焙到光照图。

投影:

无论对动态物体还是静态物体均使用基于ShadowMap的实时投影。但限定在ShadowDistance可视距离内,该距离外无投影效果。

Shadowmask

光照:

直接光照实时计算,间接光照烘焙到光照图。

投影:

所有静态物体投射到静态物体的投影都使用预烘焙的ShadowMask图计算,不考虑ShadowDistance距离。

所有静态物体投射到动态物体的投影使用LightProbe计算,不考虑ShadowDistance距离。

所有动态物体投射的投影在ShadowDistance距离内都使用ShadowMap计算,在ShadowDistance外无投影。

Distance Shadowmask

光照:

直接光照实时计算,间接光照烘焙到光照图。

投影:

在ShadowDistance距离内,全部使用基于ShadowMap的实时投影。

在ShadowDistance距离外,动态物体无投影。静态物体在动态物体上的投影使用Light Probes,静态物体在静态物体上的投影使用ShadowMask。

4. 光照图设置Lightmapping Settings

Lightmapper:

  • Enlighten – 使用Enlighten第三方组件计算全局光照,生成光照图。
  • Progressive – 使用渐进式光照图烘焙算法。当勾选Auto Generate光照图选项时,无需点击生成光照图按钮,后台会在场景发生变化时自动生成光照图。并且会优先计算编辑器窗口中显示的模型。

Indirect Resolution:

间接光照计算分辨率,影响光照图上间接光照的表现细节和精度,会极大地影响光照图烘焙时间。在测试期间建议将其设置为0.00001之类的极小值。

Lightmap Resolution:

光照图分辨率。是纹理像素和场景模型世界空间的表面积尺寸之间的比值。此值越大对于相同的模型生成的光照图越大,细节也越多。

Lightmap Padding:

对于模型表面在光照图上不同的UV展开区域,之间间隔的像素单位尺寸大小。用于避免当两块不相邻的面片在光照图上被映射到同一区域时,由于纹理采样插值会导致互相溢色。通常纹理分辨率越高此值也应该指定得越大。但过大会降低光照图的空间利用率。

Lightmap Size:

单张光照图的尺寸。注意该值是在计算光照图时输出的最大尺寸。实际的光照图尺寸还受到光照图导入纹理格式设置尺寸的影响。

Compress Lightmap:

是否压缩光照图。

Ambient Occlusion:

烘焙光照图时是否计算环境遮挡。如开启则在模型的沟槽和边角处会产生自然的黑暗区域。

Final Gather:

是否在计算全局光照弹射的最后一步使用与光照图相同的分辨率。如不开启在某些情况下光照图会产生错误的黑块,强烈建议开启。

Directional Mode:

Non-Directional –传统的单张光照图,无法产生镜面反射高光效果。

Directional –生成两张光照图,一张用于存储直接和间接光照,另一张用于存储主要光照贡献方向和比例,以便计算镜面反射效果。但是必须指定为非Subtractive的光照模式。

Indirect Intensity:

间接光照强度。取值范围0到5。可用于手动减弱或加强间接光照效果。

Albedo Boost:

材质反照率增强系数。取值范围1到10。可用于加亮材质本身的颜色。

Lightmap Parameters:

指定一个光照图计算参数配置表,内含更具体的光照烘焙计算参数。通常情况下只需在预制的配置方案中选择即可。

五、 模型设置

如果一个物体想要烘焙静态光照图或ShadowMask,则它必须在编辑器中被标记为Static静态物体。动态物体只能使用ShadowMap计算实时投影,使用LightProbe接受投影。

所有继承于Renderer的渲染器组件都有Lighting属性组进行光照相关的设置:

Light Probes:

  • Off - 不接受Light Probes的光照。
  • Blend Probes – 采样物体周边最接近的Light Probes,并混合它们的光照结果。
  • Light Probe Proxy Volumes – 当物体较大时,由最接近物体的Light Probes组成的四面体已不足以覆盖整个物体,因此使用一个虚拟包围体来采样更大范围内、更多的Light Probes,并插值它们的光照参数用于计算光照。

Cast Shadows:

  • Off – 不作为遮挡物产生投影。
  • On – 作为遮挡物产生投影。(三角形背面不产生投影)
  • Two Sided –三角形正反面均作为遮挡物产生投影。
  • Shadows Only –不渲染物体,只作为遮挡物产生投影。(三角形背面不产生投影)

Receive Shadows:

其它遮挡物是否能在此物体上投影

Lightmap Static:

是否为该物体烘焙光照图(或ShadowMask图)

UV Charting Control:

预计算实时全局光照图纹理坐标优化控制参数

  • Optimize Realtime UVs – 是否为预计算实时全局光照图优化纹理坐标。如开启此选项,不同的UV图块可以被移动、缩放或分组到不同的光照图中,较接近的UV图块还会被合并到一起。但如果不开启此选项,则不同的UV图块不会被合并,但依然可以改变位置和分组。
  • Max Distance – 合并UV图块的面片之间允许的最大距离。(世界空间尺寸)
  • Max Angle – 不同面片之间允许合并UV图块的最大夹角。
  • Ignore Normals–在导入模型时,由于模型面数较多等原因Unity有可能会把模型拆分成多个,进而导致模型的UV图块也会被拆分。模型拆分的分割线通常会在面片的法线差异较大的地方产生,如果开启了这个忽略法线的选项,在生成光照图纹理坐标时,就不会在拆分模型的同时拆分UV图块。
  • Min Chart Size –指定用于预计算全局光照的纹理坐标图块的最小尺寸,2(Minimum) 2x2像素不拼合,或4(Stitchable) 4x4像素允许拼合。

Lightmap Settings:

  • Scale In Lightmap – 为当前模型生成的静态光照图的纹理占用表面积缩放值,该值越大则当前模型的光照图分辨率越高。
  • Prioritize Illumination – 勾选后强制在进行全局光照计算时包含当前物体。否则该物体有可能被忽略,造成一些瑕疵。
  • Lightmap Parameters – 为当前物体指定单独的光照图计算参数配置表,如果为Scene Default Parameters则使用在全局光照设置中选定的光照图计算参数配置表。一般可根据渲染质量使用引擎自带的默认配置组,如需自定义参数,建议参考文章:https://unity3d.com/learn/tutorials/topics/graphics/fine-tuning-lightmap-parameters
  • Baked Lightmap –静态光照图的烘焙结果参数,不可修改,仅用来查看内部引用的资源信息。其中Lightmap Index标识该物体使用几号光照图,Tiling和Offset表示在这张光照图中的缩放和偏移量。
  • Realtime Lightmap –预计算实时全局光照图的烘焙结果参数,不可修改,仅用来查看内部引用的资源信息。

六、 修改引擎源码增加功能

1. 多通道ShadowMask烘焙采样

Unity引擎原始的ShadowMask投影遮罩区域图强制使用16位RGBA格式纹理保存,为了实现投影区域渐变的软边过渡,因此对于每个光源必须使用4位数据记录投影遮挡灰度信息。每个通道用来记录一个光源的投影遮挡信息,最大同时记录光照图渲染区域内的4个光源。

但对于游戏场景来说,大多数情况只需要有一个方向光作为主光源产生投影,并不会占用全部4个通道。对于地形之类的物件,必须要求使用一张整体的光照图,否则在不同的纹理坐标图块之间必然会出现接缝问题。而在一些低端的移动设备上,并不支持2048尺寸以上的单张贴图。这会造成投影精度完全达不到美术的要求。即便不考虑地形,ShadowMask图所占用的内存空间也太大,其中3/4的空间都是浪费掉的。

为了提高投影图分辨率,节省内存占用,我修改了Unity引擎烘焙及采样ShadowMask图的代码。并在光源属性设置页面增加了一个选项Use Multi-Channel Shadow Mask。

如果勾选了这个选项,且要烘焙的光照图中仅有这一个光源需要烘焙投影,则会把ShadowMask图的实际烘焙计算尺寸设置为光照图的2倍,即4倍的面积。然后在输出ShadowMask图时,将这张4倍分辨率的ShadowMask图等分为4份,分别存储到原始尺寸ShadowMask图的4个通道中。

在实时渲染时,引擎同样会检测这张ShadowMask图是否只对应唯一的一个烘焙投影的光源。如果是,则会使用单独实现的Shader变体来根据纹理坐标决定采样哪个通道的投影遮罩信息。

多通道存储的ShadowMask图

多通道ShadowMask采样Shader核心修改代码



fixed4 GetMultiChannelShadowMaskSelectorAndUV(inoutfloat2 lightmapUV)

{

    // Sample the shadow mask from all 4 rgba channels by remapping uv.

    lightmapUV *= 2;



    fixed4 multiChannelOcclusionSelector;



    if (lightmapUV.x >1.0)

    {

        lightmapUV.x -= 1;

        multiChannelOcclusionSelector = fixed4(0,1,0,1);

    }

    else

    {

        multiChannelOcclusionSelector = fixed4(1,0,1,0);

    }



    if (lightmapUV.y >1.0)

    {

        lightmapUV.y -= 1;

        multiChannelOcclusionSelector *= fixed4(0,0,1,1);

    }

    else

    {

        multiChannelOcclusionSelector *= fixed4(1,1,0,0);

    }



    return multiChannelOcclusionSelector;

}



inline fixed UnitySampleBakedOcclusion (float2 lightmapUV, float3 worldPos)

{

#if defined(USE_MULTI_CHANNEL_SHADOWMASK)

    fixed4 multiChannelOcclusionSelector = GetMultiChannelShadowMaskSelectorAndUV(lightmapUV);

#endif



    fixed4 rawOcclusionMask = UNITY_SAMPLE_TEX2D_SAMPLER(unity_ShadowMask, unity_Lightmap, lightmapUV.xy);



#if defined(USE_MULTI_CHANNEL_SHADOWMASK)

    return saturate(dot(rawOcclusionMask, multiChannelOcclusionSelector));

#else

    return saturate(dot(rawOcclusionMask, unity_OcclusionMaskSelector));

#endif

}

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器学习之旅

应用:基于自然语言识别下的流失用户预警

update: 17.12.20 : 关于IDF处描述,经@余海跃同学提醒,细化了解释内容,感谢! 更新内容参见:基于自然语言识别下的流失用户预警

491
来自专栏HenCoder

Android 开发进阶: 自定义 View 1-1 绘制基础

从今天开始,HenCoder 就正式开讲知识技能了。按照我的计划,第一季是 UI,UI 一共分为三部分:绘制、布局和触摸反馈。本期是绘制部分的第一期。绘制大概会...

632
来自专栏ATYUN订阅号

【教程】OpenCV—Node.js教程系列:Node.js+OpenCV面部脸识别

最近我将OpenCV普通发布版本设计的面部识别算法添加到了opencv4nodejs,它是一个npm包,允许你在Node.js应用程序中使用OpenCV。今天,...

5068
来自专栏智能算法

自动还原魔方算法数据结构

来自:CSDN博客 作者:寸辰 链接:http://blog.csdn.net/cun_chen/article/details/50261787(点击尾部阅读...

2675
来自专栏深度学习入门与实践

【深度学习系列】PaddlePaddle垃圾邮件处理实战(一)

2154
来自专栏大数据挖掘DT机器学习

玩玩文本挖掘-wordcloud、主题模型与文本分类

本文主要介绍文本挖掘的常见方法,主要包括词频分析及wordcloud展现、主题模型、文本分类、分类评价等。分类主要包括无监督分类(系统聚类、KMeans...

2996
来自专栏WOLFRAM

用 Mathematica 生成正多面体链环

1967
来自专栏一心无二用,本人只专注于基础图像算法的实现与优化。

简单探讨可牛影像软件中具有肤质保留功能的磨皮算法及其实现细节。

     在几年前写的一篇关于BEEP的文章时,我曾经说过Beep的去噪作用可以用于磨皮,并且给出了结论BEEP比可牛和美图等的效果要更为好,现在看来,那个结论...

2006

用 Python 进行时间序列数据可视化

随时间推移的观测数据的线图可视化是很受欢迎,但还有一些其他类型的图像您可以用来了解关于您的问题的更多信息。

1.9K8
来自专栏量化投资与机器学习

【深入研究】使用RNN预测股票价格系列二

接昨天的 系列一(可点击查看) 在系列一的教程中,我们想继续有关股票价格预测的主题,并赋予在系列1中建立的具有对多个股票做出响应能力的RNN。 为了区分不同价格...

2598

扫码关注云+社区