US5L3——Shader变体和关键字

本章代码关键字

1
2
#pragma shader_feature    // 声明局部关键字,只有关键词启用时才会生成对应Shader变体
#pragma multi_compile // 声明局部关键字,不管是否启用都会生成对应Shader变体

Shader 中的变体

Shader Variant(变体)指的是一个 Shader 的特定配置,通过不同的 关键字(Keywords)设置组合 来实现不同的效果
每一种关键字组合或设置都会生成一个独立的 Shader 变体,最终以二进制形式存储在构建文件中供运行时使用

说人话:Shader 变体就是基于一个 Shader 文件当中的代码,编译生成多个版本的 Shader 它基于关键字来生成各种不同的版本

举例说明:一个 Shader 中有两个关键字,每个关键字有两种状态(启用或禁用),最终生成的变体数量就是 22 = 4

  • 变体 1:没有启用任何关键字(默认)。
  • 变体 2:启用了 关键字1
  • 变体 3:启用了 关键字2
  • 变体 4:同时启用了 关键字1 和 关键字2

运行时,Unity 会根据具体设置选择与之匹配的变体来使用,Unity 的一些内置功能会隐式的生成变体,比如:

  • 光照模式 —— US3S5L6——多种光源综合实现
  • 雾效 —— US3S11L5——Unity自带全局雾效
  • 渲染管线 —— US1L1——渲染管线概述

等等

局部 Shader 关键字

  1. 声明方法

    声明局部关键字有两种方式 #pragma shader_feature​ 或 #pragma multi_compile​ 来声明关键词

    关键词命名规则:通常使用大写字母和下划线分隔(_大写单词_大写单词...​),比如:_TEST_KEYWORD

    1. #pragma shader_feature 关键词1 关键词2 关键词3....

      作用:该编译指令声明的关键字,只有关键词启用时才会生成对应 Shader 变体

      1
      2
      3
      4
      5
      6
      7
      8
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag

      // 该编译指令声明的关键字,只有关键词启用时才会生成对应 Shader 变体
      #pragma shader_feature _TEST_KEYWORD1 _TEST_KEYWORD2
      //...
      ENDCG

      如果没有任何地方显式启用 _TEST_KEYWORD​ 或 _TEST_KEYWORD2​ 则对应的 Shader 变体将不会生成

    2. #pragma multi_compile 关键词1 关键词2 关键词3....

      作用:该编译指令声明的关键字,不管是否启用都会生成对应 Shader 变体

      1
      2
      3
      4
      5
      6
      7
      8
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag

      // 该编译指令声明的关键字,不管是否启用都会生成对应 Shader 变体
      #pragma multi_compile _TEST_KEYWORD1 _TEST_KEYWORD2
      //...
      ENDCG

    ​两种声明方式的区别:主要区别在于生成 Shader 变体的数量

    • shader_feature​:编译更快,Shader 文件更小,适用开关功能较少的场景
    • multi_compile​:编译更长,Shader 文件更大,适用需要完整覆盖的功能
  2. 使用方法

    关键字使用规则,利用 #if ... #elif ... #else ... #endif​ 配合使用,判断关键词启用时处理什么逻辑

    1
    2
    3
    4
    5
    6
    7
    #if defined(关键词名)
    ....
    #elif defined(关键词名)
    ....
    #else
    ....
    #endif

    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #pragma multi_compile _TEST_KEYWORD1 _TEST_KEYWORD2

    fixed4 frag (v2f i) : SV_Target
    {
    // 用关键字控制颜色
    fixed4 col = fixed4(1,1,1,1);
    #if defined(_TEST_KEYWORD1)
    // 如果启用了 _TEST_KEYWORD1 就采样
    col = tex2D(_MainTex, i.uv);
    #endif

    #if defined(_TEST_KEYWORD2)
    // 如果启用了 _TEST_KEYWORD2 就启用雾效
    UNITY_APPLY_FOG(i.fogCoord, col);
    #endif
    return col;
    }

    通过 Shader 变体和关键词控制,即可将分支行为变为执行各种情况下的变体

Shader 中利用 C# 代码启用和禁用关键字

Unity Shader 中的关键字是一种用于控制 Shader 变体的机制,允许我们通过启用或者禁用特定功能来动态调整 Shader 的行为

Shader 中的关键字可以分为2种:

  1. 启用和禁用全局关键字

    Unity 自带的关键字,所有 Shader 中共享,
    可以在 C# 代码中通过 Shader.EnableKeyword("全局关键字名")​ 和 Shader.DisableKeyword("全局关键字名")​ 来控制关键字的启用和禁用

  2. 局部关键字只在特定的 Shader 中有效,不会影响其他 Shader

    Unity 更推荐使用局部关键字以减少关键字冲突问题,可以在 C# 代码中通过:

    • material.EnableKeyword("局部关键字名")
    • material.DisableKeyword("局部关键字名")

    来控制关键字的启用和禁用

变体和条件分支的区别

  • 条件分支语句会带来逻辑上执行的开销,虽然能够在运行时直接判断,但是会带来对性能的影响

    在动态功能选择时条件分支语句更适合

  • 变体虽然会增加编译时间并占用更多的内容,但是它的性能高,灵活性强

    在静态功能选择时变体更优,也就是只会在材质 Inspector 面板内设置或者创建后只修改一次,而不是在运行时动态修改最好