三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Unity ShaderGraph Baked GI节点详解:原理、应用与性能优化

Unity ShaderGraph Baked GI节点详解:原理、应用与性能优化

1. 项目概述:为什么我们需要关注Baked GI节点?

在Unity的ShaderGraph世界里,节点是构建视觉效果的基石。今天要拆解的这个“烘焙全局光照节点”(Baked GI Node),对于很多从离线渲染或传统光照贴图流程转过来的开发者来说,可能既熟悉又陌生。熟悉的是“烘焙全局光照”这个概念——它意味着将复杂的光照计算(如间接光、环境光遮蔽、全局反射)预先计算并存储到纹理中,运行时直接采样,从而用极低的性能开销换取高质量的静态光照效果。陌生的是,在ShaderGraph这样一个基于节点的可视化编程环境中,我们如何获取并利用这些烘焙好的数据?

这正是Baked GI节点的核心价值所在。它不是一个执行计算的节点,而是一个“数据访问门户”。当你在场景中精心布置了光照探头(Light Probes),为静态物体烘焙了光照贴图(Lightmaps)后,这个节点就是着色器与这些宝贵烘焙数据之间的桥梁。它允许你在自定义着色器中,针对模型表面的每一个顶点或片元,精确地获取到该位置应接收到的、已烘焙好的全局光照颜色信息。无论是想为角色添加受静态环境影响的边缘光,还是让动态物体与烘焙场景的光照无缝融合,亦或是实现一些依赖于环境光照的特殊材质效果(比如某些风格的卡通渲染或风格化渲染),这个节点都是关键。

简单来说,如果你满足于Unity内置的标准着色器(Standard Shader)或URP/HDRP的Lit Shader,你可能不会直接用到它。但一旦你开始用ShaderGraph定制专属的、需要与环境光照深度交互的材质,Baked GI节点就从幕后走到了台前,成为一个必须理解和掌握的工具。它解决的正是“自定义着色器如何享用全局光照烘焙成果”这一核心问题。

2. 节点核心原理与工作流程拆解

2.1 全局光照烘焙数据源解析

要理解Baked GI节点,必须先厘清Unity中烘焙全局光照数据的两种主要存储和采样方式:光照贴图(Lightmaps)和光照探头(Light Probes)。节点的工作,本质上就是对这两套系统的智能调度。

光照贴图(Lightmaps):这是针对标记为“Static”(静态)的GameObject的。在烘焙过程中,Unity会为这些物体的表面生成第二套UV(即光照贴图UV),并将计算好的光照信息(主要是间接漫反射光和部分直接光)烘焙到一张或一组大纹理中。在运行时,着色器根据片元的光照贴图UV坐标,去这张大纹理上采样,就能得到该点的烘焙光照颜色。这非常高效,但只适用于完全静止的物体。

光照探头(Light Probes):场景中动态移动的物体(如角色、车辆)无法使用固定的光照贴图。这时就需要光照探头系统。我们在场景空间中布置一系列探针点,烘焙时在每个探针位置记录下该点的光照信息(以球谐函数系数形式存储)。运行时,对于动态物体表面的每个顶点,Unity会找到包围它的最近几个探头,并利用球谐函数插值,计算出该顶点应接收的环境光照。这个过程是实时的,但开销比采样贴图稍高。

Baked GI节点的“智能”之处在于,它内部根据输入信息,自动判断并混合使用这两种数据源。对于有有效光照贴图坐标(且物体为Static)的片元,它优先从光照贴图采样;对于动态物体或光照贴图覆盖不到的区域(如模型内部),它会回退到使用光照探头数据进行球谐函数插值。

2.2 节点端口与数据流详解

节点提供了四个输入端口和一个输出端口,每个端口都承载着特定的使命:

  1. Position (Vector3, World Space):这是世界空间下的顶点或片元位置。它是光照探头采样的核心依据。光照探头网格的插值计算严重依赖于这个位置信息。如果输入的位置不准确(比如还在模型空间),那么从光照探头获取的光照信息将是完全错误的,导致物体“漂浮”在错误的光照环境中。
  2. Normal (Vector3, World Space):世界空间下的法线向量。它同样至关重要,尤其是对于光照探头数据。球谐函数编码的光照信息是带有方向性的,法线决定了从哪个方向去“读取”球谐光照。例如,一个朝上的面(法线为(0,1,0))和一个朝下的面,即使在同一位置,从球谐函数中解码出的光照颜色也可能不同,因为接收的天空光和地面反射光不同。
  3. Static UV (Vector2):通常连接顶点着色器阶段输出的第一套UV(即UV0)。这套UV用于采样静态光照贴图。只有当渲染的物体被标记为Contribute GIStatic,并且场景进行了光照贴图烘焙后,这个端口的输入才会生效。它对应着模型导入设置中生成的那套光照贴图UV。
  4. Dynamic UV (Vector2):通常连接顶点着色器阶段输出的第二套UV(即UV1)。这套UV用于采样动态光照贴图(如果渲染管线支持,如Enlighten或某些Progressive Lightmapper设置下的实时全局光照更新部分)。在大多数只使用完全烘焙静态光照的场景中,这个端口可以保持默认值或连接一个常量。
  5. Out (Vector3):输出的RGB颜色值。这就是最终采样/插值得到的烘焙全局光照颜色。通常是一个HDR颜色(可能超过(1,1,1)),需要后续进行适当的色调映射处理,才能与直接光照等其他颜色叠加。

这四个输入端口共同构成了一个完整的查询请求:“在这个位置(Position),面向这个方向(Normal),如果存在静态光照贴图,请用这个坐标(Static UV)去查;如果存在动态光照贴图更新,请用这个坐标(Dynamic UV)去查。” 节点内部逻辑处理这个请求,并返回一个融合后的光照颜色。

2.3 “Apply Lightmap Scaling”控件的作用

这是一个容易被忽略但有时很关键的布尔切换控件。在Unity的光照贴图系统中,为了优化纹理空间利用和避免边缘采样问题,引擎可能会对光照贴图UV进行微小的缩放和偏移。当“Apply Lightmap Scaling”设置为True时,Baked GI节点在采样光照贴图前,会自动应用这些缩放和偏移变换。这确保了采样坐标与引擎内部使用的坐标一致,避免出现光照贴图采样错位或边缘瑕疵。

注意:在绝大多数情况下,尤其是在使用Unity自动生成的光照贴图UV时,这个选项应该保持为True(默认值)。只有在你完全手动控制光照贴图UV,并且确信UV坐标已经包含了所有必要的变换时,才可能将其设置为False。错误地关闭此选项是导致自定义着色器光照贴图采样出现黑边或错位问题的常见原因之一。

3. 核心应用场景与实战配置

3.1 场景一:为自定义无光照着色器添加环境光

Unity的URP/HDRP内置的“无光照着色器”(Unlit Shader)默认是不接收任何烘焙全局光照的,物体看起来会完全“漂浮”于场景光照之外。通过Baked GI节点,我们可以轻松为其注入环境光感。

操作步骤:

  1. 在ShaderGraph中新建一个Unlit Master节点作为输出。
  2. 添加一个Baked GI节点。
  3. 获取世界空间位置和法线。通常使用Position节点(设置为World空间)和Normal Vector节点(设置为World空间)。对于片元着色器级别的精度,推荐使用Fragment PositionFragment Normal
  4. 获取UV。使用UV节点,分别选择UV0通道连接至Static UVUV1通道连接至Dynamic UV
  5. Baked GI节点的Out端口直接连接到Unlit Master节点的Color输入端口。
  6. (可选)由于烘焙光照可能是HDR值,可以添加一个简单的ClampReinhard Tonemapping节点防止过亮。

关键点:这样,你的“无光照”着色器就能呈现出由光照探头和光照贴图提供的柔和环境漫反射光,物体能自然地融入场景的光照氛围中,而性能开销仅增加了极低成本的纹理采样或球谐函数计算。

3.2 场景二:实现动态物体与烘焙场景的光照融合

这是Baked GI节点最经典的应用。假设你有一个烘焙了精美室内光照的静态场景,现在需要放入一个动态的角色。

标准流程的局限:如果你使用标准的Lit着色器,Unity会自动为动态物体应用光照探头。但这仅限于漫反射环境光。如果你还希望角色能接收来自烘焙场景的镜面反射环境光(即反射探针效果),或者你想对光照数据进行更艺术化的处理(比如只使用特定颜色的间接光),就需要自定义着色器。

自定义融合方案:

  1. 创建一个基于PBR(或自定义光照模型)的ShaderGraph。
  2. 按照上述方法,配置好Baked GI节点,获取到烘焙的漫反射环境光颜色(我们称之为indirectDiffuse)。
  3. indirectDiffuse与你计算出的直接光照漫反射结果相加。
  4. 对于镜面反射部分,你通常还需要采样一个Reflection Probe或使用Scene Color节点来捕获周围环境。但Baked GI节点本身不提供镜面反射信息,它专注于漫反射全局光照。
  5. 通过这种方式,你可以精确控制动态物体上烘焙光照的强度、颜色,甚至可以将它与程序化生成的光照效果混合,实现诸如“角色在黑暗中微微发光,但仍能感受到远处窗口暖光影响”的复杂效果。

3.3 场景三:风格化渲染中的光照控制

在卡通渲染(Cel-Shading)、水彩风格等非真实感渲染中,我们常常需要对光照进行高度的风格化控制。Baked GI节点提供了稳定的、预计算的环境光基底。

实战案例:卡通渲染中的阴影与高光:

  1. 获取基础环境光:使用Baked GI节点得到标准的环境光颜色。
  2. 风格化处理:将得到的RGB颜色转换为灰度值(例如使用Dot Product(0.299, 0.587, 0.114)点积),得到一个亮度值。
  3. 阶跃化(Posterize):使用Posterize节点对这个亮度值进行量化,比如只分出3个明暗阶。
  4. 重新着色:根据量化后的阶数,映射到不同的颜色上。例如,最暗阶映射为深蓝色(模拟阴影冷色调),中间阶映射为物体固有色,最亮阶映射为暖黄色。
  5. 与直接光结合:将风格化处理后的环境光,与经过同样阶跃化处理的直接光照结果进行叠加(通常是相加或屏幕混合)。

这样做的好处是,你的风格化角色或物体,其阴影和中间色调部分不再是单调的黑色或固有色,而是带有了场景环境光的色彩倾向,使得风格化作品也能拥有丰富的色彩层次和场景融入感。Baked GI节点提供的稳定、平滑的输入,是进行这类艺术化再处理的理想起点。

4. 跨渲染管线兼容性与疑难排查

4.1 HDRP与URP下的行为差异

正如官方文档警告的,Baked GI节点的行为并非由ShaderGraph全局定义,而是由各个渲染管线后端实现。这意味着在HDRP和URP下,其内部HLSL代码可能不同。

主要差异可能体现在:

  • 数据源权重:HDRP可能更倾向于使用光照探头数据,并对其有更高质量的解码(如使用更高阶的球谐函数)。URP可能对光照贴图的依赖更重。
  • HDR处理:HDRP工作在更高的动态范围下,Baked GI节点输出的颜色值范围可能更大(更亮的HDR值)。在URP中,输出可能已经被限制在较低的动态范围内。
  • 功能支持:某些高级烘焙功能(如光照贴图方向性、光照探头代理体)在一个管线中可能通过此节点暴露,在另一个管线中则不可用。

最佳实践:

核心原则:永远在目标管线中进行最终测试。如果你计划让着色器跨HDRP和URP使用,务必在两个管线项目下分别检查着色器的视觉效果。不要假设在一个管线中工作正常,在另一个管线中也一定正常。如果发现显著差异,可能需要为不同管线创建变体着色器,或使用Shader GraphKeywordCustom Function节点来编写条件代码。

4.2 常见问题与解决方案实录

在实际使用中,你可能会遇到以下问题。这里记录了我的排查笔记:

问题1:节点输出一片黑色(Out = (0,0,0))。这是最常见的问题。请按以下清单逐步排查:

  1. 场景是否烘焙?这是最根本的原因。在Lighting窗口(Window > Rendering > Lighting)中,确保已点击“Generate Lighting”或“Bake”按钮,并且烘焙成功完成。检查控制台是否有烘焙错误。
  2. 物体是否标记为静态?对于需要从光照贴图采样的物体,必须勾选Inspector右上角的“Static”复选框(至少是Contribute GI)。动态物体则依赖光照探头。
  3. 光照探头是否布置?即使对于静态物体,某些渲染设置下也可能需要光照探头来补充信息。确保场景中有覆盖所有区域的Light Probe Group,并且已参与烘焙。
  4. 输入数据是否正确?重点检查PositionNormal是否转换到了世界空间。一个快速的调试方法是,将Position直接输出为颜色,观察物体是否显示为基于世界坐标的彩虹图。将Normal输出为颜色,观察法线方向是否连续变化。
  5. UV通道是否正确?确保Static UV连接的是模型的第一套UV(通常用于纹理贴图),并且该模型在导入设置中已生成光照贴图UV(Lightmap UVs)。你可以在模型导入器的Model分页下查看和配置。

问题2:光照贴图边缘有接缝或错位。

  1. 检查“Apply Lightmap Scaling”:确保其设置为True
  2. 检查光照贴图UV生成:模型的光照贴图UV可能存在重叠或拉伸。在模型导入设置中,尝试提高“Pack Margin”值,或使用第三方建模软件生成更干净的第二套UV。
  3. 检查光照贴图分辨率:过低的分辨率会导致像素化边缘。在Lighting窗口的Lightmapping Settings中,适当提高“Lightmap Resolution”值。

问题3:动态物体上的光照不连续或跳跃。

  1. 光照探头密度不足:动态物体在移动时,其光照是通过插值周围探头得到的。如果探头布置太稀疏,当物体从一个探头组移动到另一个时,光照会发生突变。增加探头密度,尤其是在光照变化剧烈的区域(如墙角、明暗交界处)。
  2. 探头位置不合理:确保探头放置在开放空间,不要嵌入墙体或地面内部。使用“Auto Generate”功能通常能获得一个不错的基础分布,但关键区域仍需手动调整。

问题4:性能开销异常。Baked GI节点本身开销很低。但如果发现使用该节点的着色器明显更慢,需检查:

  1. 是否在片元着色器中采样了高分辨率光照贴图?确保光照贴图分辨率设置合理,并非越高越好。
  2. 是否对每个片元都执行了复杂的球谐函数计算?对于动态物体,考虑将Baked GI节点的计算从片元着色器移至顶点着色器,然后在片元间进行插值。虽然精度略有下降,但对于平滑的模型可以大幅提升性能。在ShaderGraph中,这意味着将提供PositionNormal的节点设置为Vertex阶段,然后通过Interpolator传递到片元阶段。

5. 高级技巧与性能优化指南

5.1 混合烘焙光照与实时全局光照

现代渲染管线如HDRP和URP支持混合光照模式(Mixed Lighting)和部分实时全局光照技术(如Screen Space Global Illumination)。Baked GI节点主要处理完全烘焙的静态光照。如何与实时元素结合?

策略是分层处理:

  1. 使用Baked GI节点获取“静态基底”:这部分是场景中永不变化的光照贡献。
  2. 使用其他节点获取实时GI贡献:例如,HDRP中可以使用Diffusion ProfileScreen Space GI相关节点。URP中可能需要依赖后处理体积或自定义计算。
  3. 动态混合:根据物体的静态/动态属性、与光源的距离等因素,使用Lerp节点混合烘焙光照和实时光照。例如,对于静态物体,权重完全偏向烘焙光照;对于动态物体,可以混合烘焙的环境光和实时的直接光。

关键在于理解Baked GI节点只是你光照方程中的一个输入项,而不是全部。你需要构建一个更大的着色器框架来统筹所有光照源。

5.2 在顶点着色器与片元着色器间的权衡

Baked GI计算放在顶点阶段还是片元阶段,是一个经典的性能与质量权衡。

  • 顶点着色器计算:将PositionNormal在顶点着色器中输入Baked GI节点,输出颜色在顶点间插值后传给片元。优点:性能极佳,每个模型只需对少数顶点计算球谐函数或采样光照贴图(通过导数)。缺点:光照在三角形内部是线性插值的,对于低多边形模型或需要高频光照细节(如光照贴图中的小阴影)的情况,会出现明显的马赫带(色块)现象,质量低下。
  • 片元着色器计算:在每个像素点都执行一次完整的Baked GI采样。优点:质量最高,能精确呈现光照贴图的每一个细节。缺点:性能开销最大,尤其是对于高覆盖率的物体。

我的经验法则:

  • 动态、平滑曲面物体(如角色、车辆):优先使用顶点着色器计算。因为动态物体主要依赖光照探头,球谐光照本身变化平滑,顶点插值足以保证视觉质量,同时能节省大量性能。
  • 静态、复杂表面物体(如建筑、地形):必须使用片元着色器计算。因为它们依赖高分辨率的光照贴图来表现阴影和细节,顶点插值会严重损失这些信息。
  • 折中方案:对于某些需要高质量但又担心性能的物体,可以尝试在ShaderGraph中使用Custom Function节点,手动实现一个在顶点着色器计算基础值,在片元着色器进行简单修正(如基于法线细节纹理)的混合方案。

5.3 自定义光照探针与光照贴图采样逻辑

对于有极致优化或特殊风格化需求的团队,可能会觉得Baked GI节点的内部黑盒逻辑不够灵活。这时,可以绕过它,直接访问底层数据。

替代方案:手动编码

  1. 光照探头数据:Unity的ShaderLab和HLSL提供了内置函数,如ShadeSH9(用于低阶球谐)或SampleSH系列函数。你可以直接在Custom Function节点中调用这些函数,传入世界空间法线,来获取光照探头颜色。这让你可以控制使用球谐函数的阶数,甚至对结果进行自定义的数学或艺术化处理。
  2. 光照贴图数据:通过TEXTURE2D宏和SAMPLER宏声明unity_Lightmapunity_LightmapInd(方向性光照贴图)等内置纹理,并使用DECODE_LIGHTMAP宏来采样和解码。这需要你手动处理光照贴图UV的缩放偏移(即unity_LightmapST),但给了你完全的采样控制权。

警告:这是一条进阶路径,需要对Unity的着色器底层和CG/HLSL有较深理解。它破坏了ShaderGraph的可视化优势,且代码高度依赖于Unity版本和渲染管线。除非有非常明确且强烈的需求(例如实现某种论文中的特殊光照解码算法),否则不建议初学者或普通项目使用。Baked GI节点提供的稳定性和跨版本兼容性,在大多数情况下价值更高。

通过深入理解Baked GI节点的原理、熟练掌握其应用场景、并有效规避常见的陷阱,你就能在ShaderGraph中自如地驾驭烘焙全局光照这股强大的力量,让自定义着色器创作既充满艺术自由,又能牢牢扎根于高效、高质量的生产管线之中。

← 返回列表