17.Shader开发进阶总结

  1. 17.总结
    1. 17.1 核心要点速览
      1. ShaderGUI 与整页材质 Inspector
      2. MaterialPropertyDrawer 与 ShaderGUI 的分工
      3. Shader 变体与关键字:原理与选型
      4. 内置材质属性绘制(Drawer)
      5. Properties 前的限制与装饰特性
      6. 帧调试器(Frame Debugger)
      7. 表面着色器:定位与生成物
      8. #pragma surface 与光照模型
      9. Input 与 SurfaceOutput*
      10. 实例:法线贴图、顶点膨胀、finalcolor
      11. 实例:动态液体(思路摘要)
    2. 17.2 面试题精选
      1. 基础题
        1. 1. ShaderGUI 和 MaterialPropertyDrawer 分别解决什么问题
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. shader_feature 与 multi_compile 选型差异
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. Frame Debugger 里 RT0 和 Details 一般怎么用
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        4. 4. 表面着色器和直接写顶点片元着色器怎么选
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
      2. 进阶题
        1. 1. Toggle 与 Shader 关键字如何对齐
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. 表面着色器里的 addshadow 是干什么的
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. vertex 与 finalcolor 在表面着色器流程里扮演什么角色
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
      3. 深度题
        1. 1. Enum 与 KeywordEnum 在材质面板与 GPU 侧的差异
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. 动态液体示例里如何用高度与 clip 做液面
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. exclude_path 对表面着色器有什么意义
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章

17.总结


17.1 核心要点速览

ShaderGUI 与整页材质 Inspector

Unity 默认会按 Properties 块顺序画材质面板。一旦项目里参数多、需要分组、条件显示或附带「一键重置」这类操作,就需要接管整张 Inspector。

做法是写一个继承 ShaderGUI 的类,重写 OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties),并在 Shader 根级写 CustomEditor "你的类全名"。绑定后,该 Shader 的所有材质实例都会走你的 OnGUI

  • materialEditor.target 指向当前选中的材质(多选时需注意批量逻辑是否与你的 GUI 兼容)。
  • MaterialEditor.FindProperty("_Name", properties) 按名字拿到 MaterialProperty,可读写 floatValuecolorValuetextureValue 等,再交给自定义控件或业务逻辑。
  • materialEditor.ShaderProperty(prop, label) 等价于「这一行仍用 Unity 默认控件画」,便于在自定义布局里穿插默认外观。
  • 若不调用 base.OnGUI,即使 Properties 里声明了属性,也可以一行都不画——版面完全由你决定。正文示例里对 renderQueue 用整数字段、用按钮重置纹理和 Float,说明 MaterialEditor / 材质 API 可以和 EditorGUILayout 混用:本质是 Editor 脚本,只是多了材质语义。

MaterialPropertyDrawer 与 ShaderGUI 的分工

维度 ShaderGUI MaterialPropertyDrawer
作用范围 整张材质 Inspector 单个属性在列表里那一行的绘制与交互
典型用途 折叠、分组、条件显示、额外按钮、与项目规范统一的排版 自定义滑条曲线、枚举、与关键字联动的单槽控件
入口签名 OnGUI(MaterialEditor, MaterialProperty[]) OnGUI(Rect, MaterialProperty, string label, MaterialEditor editor)

MaterialPropertyDrawer 不负责「这一页长什么样」,只负责「这个 _Foo 在列表里怎么画、怎么改值」。与 ShaderGUI 组合时,常见写法是在遍历 properties 时对特定属性手动调用某个 Drawer 的 OnGUI(EditorGUILayout.GetControlRect(), prop, prop.displayName, materialEditor),其余属性仍走 ShaderProperty

属性声明前可以挂 [YourDrawerName(参数)],Unity 会按类型名找绘制器(特性里写的类名通常不带 Drawer 后缀)。仅依赖特性自动挂接时,个别版本或边缘情况下曾出现不稳定反馈;工程里更稳妥的是:内置 Drawer 直接用,或 ShaderGUI 里显式调用,减少「魔法挂接」带来的排查成本。

Shader 变体与关键字:原理与选型

变体是什么:同一份 HLSL/CG 源码在编译时,会按关键字组合、平台宏、渲染路径等切成多份 GPU 程序。运行时 Unity 根据材质(及全局)上启用的一组关键字,选中匹配的那一份。雾、光照模式、内置特性也会参与变体膨胀,所以「pragma 写得松」会直接反映在编译时间和包体上。

#pragma shader_feature#pragma multi_compile 的差异(行为以当前 Unity 文档与 Stripping 设置为准,面试与工程讨论里通常按下面理解):

  • shader_feature:倾向于只为实际会被材质用到的关键字组合生成变体,有利于控制变体数量和资源体积,编译也相对轻;适合「可选功能开关少、希望包体可控」的场景。
  • multi_compile:倾向于为声明中的组合提供更完整覆盖,变体更多、编译更慢、体积更大;适合「管线或系统要求任意组合都必须存在」的情况。

Shader 里用 #if defined(KEY) 做分支时,走的是编译期分岔,选中变体后 GPU 上通常不再为这一路付动态分支代价;代价是要维护关键字集合、注意变体剔除与加载。若选项每分钟都在变、且组合爆炸,有时反而更适合用 uniform + 运行时 if,在指令与分支之间做权衡。

全局与局部关键字Shader.EnableKeyword / DisableKeyword 影响面大,容易和别的材质打架。更常见做法是用 material.EnableKeyword / DisableKeyword,把关键字绑在具体材质实例上,缩小冲突范围。

内置材质属性绘制(Drawer)

特性 / 类 面板行为 与 GPU 侧关系 典型声明
Toggle Float 0/1,可绑定关键字 勾选时启用约定名或自定义关键字,配合 shader_feature / multi_compile 切变体 [Toggle][Toggle(自定义关键字)]
Enum 下拉,写入你配置的离散数值 一般配合运行时按数值分支;不自动引入 KeywordEnum 那套变体 [Enum(显示,值,...)]
KeywordEnum 下拉,每项映射到 _属性名_选项名 形式关键字 必须和 Pass 内 pragma 声明的关键字一致,用 #if defined 切变体 [KeywordEnum(A,B,...)]
PowerSlider Range 滑条,拖动手感按指数曲线映射 仍是标量 uniform,只是 Inspector 映射非线性 [PowerSlider(指数)] + Range(min,max)
IntRange Range 在面板上按整数步进 写入整型语义上的 float uniform [IntRange] + Range(...)

Toggle、KeywordEnum 与 Pass 里的 #pragma shader_feature / multi_compile 名字必须对齐,否则面板改了关键字,Shader 里根本没有对应变体或宏名,往往表现为「不切换」「切了没效果」或启用了错误组合。正文示例里 KeywordEnum 生成的关键字形如 _KEYWORDTESTENUM_TEX,与 pragma 列表逐项对应,这是排查时的第一手对照表。

Properties 前的限制与装饰特性

特性 作用
HideInInspector 属性仍序列化在材质上,脚本可改,Inspector 不显示
NoScaleOffset 2D 纹理不显示 Tiling / Offset,适合不需要缩放的蒙版、数据纹理
Normal 非法线压缩格式时给提示,减少误用切线空间法线贴图
HDR 颜色或纹理按 HDR 范围拾取,常用于自发光、Bloom 输入
Space / Space(n) 属性前插入空行或 n 像素间距,做视觉分组
Header(“文本”) 在属性列表里插入分组标题,不参与编译

帧调试器(Frame Debugger)

路径:Window → Analysis → Frame Debugger。启用后,Unity 会抓取当前帧的绘制与清理事件序列,你可以从左到右逐步选中事件,看这一步画到了哪里、用了什么状态。运行时开启往往会暂停游戏,这是预期行为:它在记录与回放渲染命令,而不是单纯「叠加一层 HUD」。

左侧事件树:叶节点是一次具体事件;父节点旁的数字表示子事件数量。名称里出现 UpdateDepthTextureDrawingRender Shadow MapImageEffects 等,可以快速判断当前是在更新深度纹理、画几何、画阴影还是后处理链路。

中间视图RT0 一般指当前查看的第一个颜色类渲染目标;多 MRT 或中间纹理时序号递增。深度、法线、遮罩常被编码在 0~1 区间,单通道显示或调 Levels 能把对比拉开,否则肉眼很难分辨。

Details 面板:汇总当前事件的 Shader 名、Pass、混合方程、深度测试等;KeywordsTexturesFloats 用来核对「我以为的材质变体」和「实际提交的一致不一致」。这和只看 Inspector 是互补的:Inspector 是资产侧意图,Frame Debugger 是这一帧真正送 GPU 的状态。

连真机或 Player 调试时,Editor 侧能力通常要求 Development Build,并视情况开启 Run In Background,否则帧抓取或连接行为可能不完整。

表面着色器:定位与生成物

表面着色器写在 SubShaderCGPROGRAMENDCG 之间,不手写 Pass。编译阶段 Unity 根据 #pragma surface 与可选参数,生成 Forward / 阴影投射 / 附加光等多组 Pass,并把光照、雾、Lightmap 等接到生成代码里。

相对手写 vert/frag:优点是常规受光材质写得快,少重复造轮子;缺点是生成代码量大、黑盒多,要做非常规混合、自定义管线或极致优化时,往往不如显式 Pass 清晰。移动端项目需要结合变体数量与指令数评估是否大面积使用。

骨架始终包含:#pragma surface、自定义 Input 结构、surf 函数;可选自定义 Lighting* 光照函数。Unity 编辑器里可以查看生成代码,对照 surf 输出的表面属性如何进入光照与最终颜色,这对排查「为什么和标准光照不一致」很有帮助。

#pragma surface 与光照模型

指令形式:#pragma surface 表面函数名 光照模型 [可选参数…]

  • surfSurfaceOutput 类型必须与光照模型匹配:SurfaceOutput 对应 Lambert / BlinnPhong;SurfaceOutputStandard 对应 physically based 的 StandardSurfaceOutputStandardSpecular 对应高光工作流。
  • 内置光照模型名字即 LambertBlinnPhongStandardStandardSpecular。自定义光照时写 Lighting函数名,按是否需要视角相关项选择是否声明带 viewDir 的重载。

常用可选参数(按需选用,每个都会改变生成 Pass 集合):

  • vertex::修改 appdata_full,可做位移、往 Input 写自定义字段。
  • finalcolor::光照之后再改 color,适合整体色调、后乘颜色。
  • addshadow / fullforwardshadows / noshadow:控制阴影相关生成策略。
  • alphatest:noambientnoforwardaddnofognolightmap:分别裁剪环境光、附加光、雾、光照贴图等路径上的贡献。
  • exclude_path:forward|deferred|prepass:在确定项目只走某条路径时,砍掉不需要的路径生成,减小变体;排除错了会导致该路径下材质不渲染或渲染错误。

Input 与 SurfaceOutput*

Input 的成员名有约定:Unity 按名字自动填充,例如 uv_纹理属性名uv2_viewDirscreenPosworldPosworldReflworldNormal,以及带 COLOR 语义的顶点色。若在 surf 里修改了切线空间法线 o.Normal,则依赖世界空间法线或反射的字段不应再用未修正的 worldNormal / worldRefl,应改用 WorldNormalVector(IN, o.Normal)WorldReflectionVector(IN, o.Normal),否则光照与采样方向会与视觉法线不一致。

声明了 vertex: 时,可以在顶点阶段往 Input 的自定义字段写数据,片元阶段在 surf 里读,这是表面着色器里扩展逐顶点数据的标准路径。

三种 SurfaceOutput* 结构由引擎定义,字段集合固定,不能随意加减成员。选型规则:SurfaceOutput 配非 PBR 那套;SurfaceOutputStandard / SurfaceOutputStandardSpecular 配对应工作流里的 Metallic / Specular 参数。

实例:法线贴图、顶点膨胀、finalcolor

法线贴图:在 Input 中声明 float2 uv_BumpMap(名字与 _BumpMap 属性对应),在 surfo.Normal = UnpackNormal(tex2D(_BumpMap, uv))。若光照模型是 Standard,继续在 SurfaceOutputStandard 上设置 MetallicSmoothnessEmission 等,逻辑与常规 PBR 材质一致,只是表面函数代替了手写片元里的光照拼装。

顶点沿法线膨胀#pragma surface ... vertex:MyVert,在 MyVertinout appdata_full v,写 v.vertex.xyz += v.normal * _Expansion。用途包括描边壳、体积感夸张;注意对象空间尺度与模型缩放的感受,和纯片元描边是不同取舍。

finalcolor 调色:若把 _Color 乘在 surf 的 Albedo 上,等价于「进光照之前就染色」。有时希望「先按物理光照算完,再整体乘一层颜色」,则把乘法挪到 finalcolor,对 colorcolor *= _Color。两种顺序在数学上不等价,选用哪一种是美术与物理可信度之间的选择。

实例:动态液体(思路摘要)

场景结构:外层透明网格表示容器,内层单独材质画液体。液体 Shader 使用透明队列与特定混合:RenderTypeQueueTransparentBlend DstColor SrcColorZWrite Off;正文实现选用 StandardSpecular 并配合 noshadow,示例为控制篇幅未展开阴影投射链路。

液面高度:把模型中心用 unity_ObjectToWorld 变到世界空间,与 Input.worldPos 在竖直方向比较,加上 _Height 等参数得到相对高度;再叠加正弦项,用 _Time.y_WaveFrequency、世界坐标与 _InvWaveLength_WaveAmplitude 控制波纹相位与幅度。

硬边界:用 step 把「在液面之上 / 之下」变成 0 或 1,再配合 clip 丢弃不应显示的片元;小偏移(如减去 0.001)可减少边界抖动。示例里的 surf 未使用 _Speed 属性;若要做流速,只需在时间项上乘以系数或单独引入速度 uniform 即可。


17.2 面试题精选

基础题

1. ShaderGUI 和 MaterialPropertyDrawer 分别解决什么问题

题目

Unity 里想自定义材质在 Inspector 里的表现,ShaderGUI 与 MaterialPropertyDrawer 各负责哪一层?各通过什么入口参与绘制?

深入解析
  • ShaderGUI 面向整张材质面板:继承 ShaderGUI,在 OnGUI(MaterialEditor, MaterialProperty[]) 里决定有哪些控件、是否调用默认 ShaderProperty、是否完全自定义。
  • MaterialPropertyDrawer 面向单个属性槽:继承 MaterialPropertyDrawer,重写带 RectOnGUI,只负责该属性在列表里那一行的外观与交互;可通过 Shader 属性前的特性挂接,也可在自定义 ShaderGUI 里手动调用。
  • Shader 根级 CustomEditor 指向 ShaderGUI;Drawer 挂在具体属性声明前或与 ShaderGUI 逻辑组合使用。
答题示例

ShaderGUI 管整页材质 Inspector,重写 OnGUI,Shader 里用 CustomEditor 指定类名。

MaterialPropertyDrawer 管单个属性怎么画,重写带 RectOnGUI,常用特性挂在属性前,或在 ShaderGUI 里对某个 MaterialProperty 手动调 Drawer。

参考文章
  • 1.自定义材质面板-ShaderGUI类
  • 2.自定义材质面板-MaterialPropertyDrawer类

2. shader_feature 与 multi_compile 选型差异

题目

#pragma shader_feature#pragma multi_compile 在 Shader 变体生成上有什么本质区别?各适合什么场景?

深入解析
  • 二者都声明局部关键字并参与变体生成;区别在于未使用组合是否仍全部编译进资源的典型策略:shader_feature 倾向只为材质实际用到的关键字组合生成变体,利于控制变体数量与包体;multi_compile 倾向为声明中的组合提供更完整覆盖,变体更多,编译更慢、体积更大。
  • 少开关、可选功能用 shader_feature 更常见;系统级、必须所有组合都可切换的管线常用 multi_compile
  • 实际行为以当前 Unity 版本与 Stripping 设置为准,但面试常考「变体数量与编译/包体权衡」这一维度。
答题示例

shader_feature 一般更省变体,只为你实际用到的关键字组合生成版本,适合功能开关少、想控包体的时候。

multi_compile 往往把声明里的组合都编出来,变体多、编译重,适合必须全覆盖的组合。

选型就是在编译时间、包体大小和功能覆盖之间做权衡。

参考文章
  • 3.自定义材质面板-自带Shader材质属性绘制类-Shader变体和关键字

3. Frame Debugger 里 RT0 和 Details 一般怎么用

题目

用 Frame Debugger 查一帧渲染时,RT0、通道隔离、Details 分别帮你确认什么?

深入解析
  • RT0 表示当前正在查看的渲染目标之一,常对应颜色缓冲;多目标或中间纹理时序号递增。
  • 单通道或 Levels 用于把非「直观颜色」的数据(深度编码、法线、遮罩)在画面上拉开对比度。
  • Details 汇总当前事件的渲染状态:材质、Shader 名、Pass、混合、深度测试、光照路径相关开关等,用来对照「我以为用的 Shader/变体」和「实际提交 GPU 的状态」是否一致。
答题示例

RT0 就是看当前这一步画到哪张目标上,常是第一个颜色缓冲。

只开 R/G/B 或调 Levels,是为了看清深度、法线这类挤在 0~1 里的数据。

Details 里看具体哪个材质、哪个 Pass、混合和深度怎么开,跟 Inspector 里设的是否一致。

参考文章
  • 8.帧调试器

4. 表面着色器和直接写顶点片元着色器怎么选

题目

Unity 里 Surface Shader 与手写多 Pass 的顶点/片元着色器各适合什么场合?

深入解析
  • Surface Shader:Unity 生成 Pass 并接内置光照与阴影,适合快速做受光材质、少写重复光照代码;缺点是黑盒多、定制渲染路径或极致优化时受限,生成代码体积大。
  • 显式 VF:完全控制 Pass、插值与关键字,适合自定义管线、特殊混合、极致性能或与非内置光照模型深度耦合的场景。
  • 移动端项目常要在「开发效率」与「变体/指令数可控」之间权衡 Surface 的使用面。
答题示例

表面着色器省事,光照阴影 Unity 帮你拼 Pass,做常规受光材质快。

手写顶点片元适合要完全掌控 Pass、混合、优化和特殊管线的时候。

移动平台要警惕表面着色器展开后的体积和开销。

参考文章
  • 9.表面着色器-基本原理和结构

进阶题

1. Toggle 与 Shader 关键字如何对齐

题目

材质上用 [Toggle]_ShowTex("ShowTex", Float) = 1 时,默认会关联什么关键字?在 Shader 里要如何声明才能用 #if defined 走变体分支而不是纯 if

深入解析
  • 默认会为勾选状态启用形如 _SHOWTEX_ON 的关键字(与属性名大写加 _ON 的规则一致,正文以 _SHOWTEX_ON 为例);也可用 [Toggle(自定义名)] 指定自定义关键字。
  • Pass 内用 #pragma shader_feature _SHOWTEX_ONmulti_compile 列出与 Toggle 一致的关键字,片元里用 #if defined(_SHOWTEX_ON)#else 分支;这样切换材质上的 Toggle 会切换变体。若只用 if(_ShowTex==1) 则是运行时分支,不依赖该 pragma 关键字体系。
  • pragma 中的关键字名必须与 Toggle 生成或指定的字符串一致,否则面板切换不会驱动预期变体。
答题示例

默认 Toggle 会用到类似 _SHOWTEX_ON 这种关键字,也可以用 [Toggle(自定义)] 改名。

Pass 里用 shader_featuremulti_compile 声明这些关键字,代码里用 #if defined 分支,就和 Inspector 上的勾选联动成变体切换。

名字要和 pragma 里完全一致,否则对不上。

参考文章
  • 4.自定义材质面板-自带Shader材质属性绘制类-ToggleDrawer

2. 表面着色器里的 addshadow 是干什么的

题目

#pragma surface 的可选参数 addshadow 典型用在什么情况?和单纯依赖 FallBack 有什么差别?

深入解析
  • 顶点动画、改模型轮廓或 Alpha Test 等会改变「几何在光空间中的样子」时,默认阴影投射可能不准。
  • addshadow 让 Unity 为表面着色器生成更贴合当前顶点变换的阴影投射路径,避免仅靠 FallBack 的简化 Mesh 与当前片元裁剪不一致。
  • 仍要与材质上的 Cast Shadows 等设置配合,且会增加生成代码量。
答题示例

顶点动画或透明度裁剪改了实际轮廓时,FallBack 的阴影可能对不上。

addshadow 让 Unity 按你当前顶点逻辑去补投射阴影,减少错位。

代价是多生成相关 Pass,要权衡。

参考文章
  • 10.表面着色器-编译指令

3. vertex 与 finalcolor 在表面着色器流程里扮演什么角色

题目

vertex:MyVertfinalcolor:MyFinal 分别修改的是哪一阶段的数据?举一个适合用 finalcolor 的调色需求。

深入解析
  • vertex 在顶点阶段改 appdata_full,影响后续插值进表面的几何与部分可写进 Input 的自定义数据。
  • surf 写出表面属性(Albedo、Normal 等),再经光照模型合成颜色。
  • finalcolor 在得到光照结果之后对 color 做最后乘法或叠加,适合「光照后再统一乘一层色调」的需求;正文示例将 _Color 放到 finalcolor 即避免在 surf 的 Albedo 上重复乘颜色逻辑。
答题示例

vertex 管顶点变换和往 Input 里塞数据。

surf 给材质属性,光照算完才到 finalcolor。

要在光照后整体调色、用 finalcolor 乘 _Color 这类需求很合适。

参考文章
  • 14.表面着色器-实例分析-顶点膨胀

深度题

1. Enum 与 KeywordEnum 在材质面板与 GPU 侧的差异

题目

[Enum(...)][KeywordEnum(...)] 在 Inspector 上都是下拉,对 Shader 编译与运行时选路有何不同?

深入解析
  • Enum 只把 Float 设成你配置的离散数值,片元里通常用 if/else 按数值分支;不自动引入新的 shader keyword,变体数量不因此倍增(除非你自己再配合其他关键字)。
  • KeywordEnum 为每个选项生成独立关键字(如 _属性名_选项名),需配合 #pragma shader_feature 等声明,用 #if defined 选路,属于变体级切换;下拉改选项会改材质上的关键字集合,从而切换编译好的变体。
  • 选型:Enum 适合简单多选一且可接受运行时分支或变体由别机制控制时;KeywordEnum 适合要把选项固化进变体、避免每帧动态分支或需要与关键字管线统一的场景。
答题示例

Enum 就是改 Float 数值,Shader 里多用手写分支按数值走,不自动造关键字变体。

KeywordEnum 每个选项对应一个关键字,要配 shader_featuremulti_compile,用宏分支切变体,和关键字系统绑在一起。

想要变体级切换、少跑动态分支就用 KeywordEnum;只是面板友好、逻辑简单可用 Enum。

参考文章
  • 5.自定义材质面板-自带Shader材质属性绘制类-EnumDrawer和KeywordEnumDrawer

2. 动态液体示例里如何用高度与 clip 做液面

题目

动态液体示例 Shader 如何用世界空间高度与正弦波纹决定显示哪些片元?stepclip 各起什么作用?

深入解析
  • 将模型中心变换到世界空间,与当前 worldPos 做竖直方向差并加 _Height 缩放,得到相对液面基准的高度关系。
  • 叠加 sin(_Time.y * _WaveFrequency + worldPos.x * _InvWaveLength) * _WaveAmplitude 模拟液面起伏。
  • step(0, liquidHeight) 把关系变为 0 或 1;再 clip(liquidHeight - 0.001):小于等于 0 的片元被丢弃,形成硬边界;小偏移避免边界抖动。
答题示例

用物体中心在世界空间的高度和当前像素比,再加参数调液面,sin 做波纹。

step 把「在液面上还是下」变成 0/1,clip 把不该显示的片元直接扔掉。

减一点点 epsilon 让边界更干净。

参考文章
  • 15.表面着色器-实例分析-动态液体-基本原理
  • 16.表面着色器-实例分析-动态液体-具体实现

3. exclude_path 对表面着色器有什么意义

题目

表面着色器默认会为多条渲染路径生成代码,exclude_path 系列参数解决什么问题?

深入解析
  • Unity 为 Surface Shader 自动生成前向、延迟等路径相关变体,Shader 体积与编译时间上升。
  • 若确定材质只出现在某条路径(例如仅前向),可用 exclude_path:deferred 等砍掉不需要的路径生成,减小变体与文件大小。
  • 选错排除会导致在该路径下材质无法正确渲染,需与项目实际渲染设置一致。
答题示例

表面着色器默认帮多路径都生成 Pass,体积大。

确定只用前向就可以 exclude 延迟那条,减小变体和编译量。

排除错了在那条路径上会渲染不对,要和项目管线一致。

参考文章
  • 10.表面着色器-编译指令


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏