当前位置:   article > 正文

UnityShader30:预编译指令 multi_complie 和 shader_feature_#pragma multi_compile _ _

#pragma multi_compile _ _

multi_complie 和 shader_feature 编译指令往往用于正式游戏项目的优化

一、关键字与Shader变体

multi_complie 的用法:

#pragma multi_compile NAMEA, NAMEB, NAMEC, …,

参考代码:

  1. Shader "Jaihk662/ShaderVariantTest"
  2. {
  3. Properties
  4. {
  5. _MainTex("Texture", 2D) = "white" {}
  6. //枚举宏,注意在预编译指令中,宏要大写
  7. [KeywordEnum(R,G,B)] _Color("Color", float) = 0
  8. }
  9. SubShader
  10. {
  11. Tags { "RenderType" = "Opaque" }
  12. LOD 200
  13. Pass
  14. {
  15. CGPROGRAM
  16. #pragma vertex vert
  17. #pragma fragment frag
  18. #pragma multi_compile _COLOR_R _COLOR_G _COLOR_B
  19. #pragma multi_compile __ DB_ON
  20. #include "UnityCG.cginc"
  21. struct v2f
  22. {
  23. float4 vertex: SV_POSITION;
  24. float2 uv: TEXCOORD0;
  25. };
  26. sampler2D _MainTex;
  27. float4 _MainTex_ST;
  28. v2f vert(appdata_base v)
  29. {
  30. v2f o;
  31. o.vertex = UnityObjectToClipPos(v.vertex);
  32. o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
  33. return o;
  34. }
  35. fixed4 frag(v2f i): SV_Target
  36. {
  37. #if DB_ON
  38. return fixed4(1, 1, 1, 1);
  39. #elif _COLOR_R
  40. return fixed4(1, 0, 0, 1);
  41. #elif _COLOR_G
  42. return fixed4(0, 1, 0, 1);
  43. #elif _COLOR_B
  44. return fixed4(0, 0, 1, 1);
  45. #else
  46. fixed4 color = tex2D(_MainTex, i.uv);
  47. return color;
  48. #endif
  49. }
  50. ENDCG
  51. }
  52. }
  53. }
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class TestShader: MonoBehaviour
  5. {
  6. public Material mat;
  7. void Update()
  8. {
  9. if (Input.GetKey(KeyCode.Q))
  10. mat.EnableKeyword("DB_ON");
  11. if (Input.GetKey(KeyCode.W))
  12. mat.DisableKeyword("DB_ON");
  13. }
  14. }

其中 NAMEA 为枚举名,一个非空的枚举名为一个全局的关键字(Keyword)

关于关键字(Keyword)

  1. 指定的第一个 keyword 默认生效
  2. 对于一条预编译指令中的内容,当然同时有且只有一个 keyword 生效
  3. 在脚本中使用 material.EnableKeyword、Shader.EnableKeyword 或 CommandBuffer.EnableShaderKeyword 来指定激活哪个关键字,需要注意的是后两个方法是全局的,当然了 Keyword 不特殊声明也是全局的
  4. 同 ③ 可以使用 .DisableKeyword 方法取消激活,如果没有被激活的回到 ①
  5. 除此之外,也可以在 Properties 中定义对应的 Enum 或者 Toggle,并在材质面板中设定好 keyword,就如下图

这样做的目的是什么?对于 #pragma multi_compile _COLOR_R _COLOR_G _COLOR_B 指令,对应的 Shader 会被编译成三个变体(Variant):一是只包含 _COLOR_R 模块代码的变体 A;二是只包含 _COLOR_G 模块代码的变体 B,三是只包含 _COLOR_B 模块代码的变体 C,这就相当于是省掉了着色器中的 if 语句,直接将所有分支全部展出来

当然了,如果一个 shader 中有多个这样的预编译指令,那么生成的变体数量会是累乘的,就像上面的示例代码就会有 2 x 3 = 6 个变体

二、未定义宏(NoKeyword)与关键字限制

上面的代码中有下面这么一段,那么对于这第二行,__ 是什么东西呢?

  1. #pragma multi_compile _COLOR_R _COLOR_G _COLOR_B
  2. #pragma multi_compile __ DB_ON
  3. #pragma shader_feature FANCY_STUFF

其实它代表着当前这一行预编译指令中,所有的关键字都不生效,可以理解为“空”,即未定义宏(NoKeyword),对于 #pragma multi_compile __ DB_ON,其 Shader 仍会编译成两个变体:一是不包含 DB_ON 模块代码的变体;二是包含 DB_ON 模块代码的变体,当然默认为__,即不包含 DB_ON 的变体生效

  • 全局的 Keyword 只能有256个!因此可以尽量使用 __,这样可以省掉一个关键字
  • 对于只有一个关键字的情况,如过是 #pragma shader_feature A 这种,它相当于是省略了前面的 __,是一种简便写法,但是 #pragma multi_compile A 并不是!它是一种错误的写法,相当于是 shader 此时只对应 A 这个变体,但无论如何,永远不建议这么写

局部的关键字:

同样能使用 #pragma multi_complie_local 的方法声明只在对应 shader 内部生效的关键字(keyword),对于局部的关键字(keyword):

  • local keyword 仍有数量限制,每个 Shader 最多只能包含64个 local Keyword
  • 只能使用局部方法 material.EnableKeyword 来指定激活
  • 如果同时声明了全局和局部 keyword,局部的优先级高

三、multi_complie && shader_feature

multi_complie 和 shader_feature 作用完全一样,唯一的区别是:如果使用 shader_feature,build 项目时和 shader 在同一个 AB 包中没有任何 Material 用到的变体就不会打出来。这也意味着在非编辑器中运行代码 Material.EnableKeyword("B") 可能会不起作用,因为没有 Material 在使用变体 B,变体 B 没有被 build 出来,运行时也找不到变体 B,而在编辑器环境下,multi_complie 和 shader_feature 没有区别

shader_feature 的作用就是避免没有被使用的冗余变体(ShadeVariant)被生成

一般来讲,对于 Properties 中定义对的 Enum 或者 Toggle 关键字,使用 #shader_feature,而对于在脚本中设置的关键字,使用 #multi_complie,这个很好理解,又或者可以这么说:

  • #multi_compile 往往同时适用于大部分 shader,与 shader 自身所带的属性无关
  • 而 shader_feature 定义的宏多用于针对 shader 自身的属性

如果偏要使用 shader_feature,但又避免不了用到所有的变体,也可以通过把对应 Shader 加入到 “always included shaders” 中,这样它所有的变体都会被直接打包到游戏,又或者放在 Assets/Resources 文件夹,不走 AB 加载

当然这两种方法都不推荐,如果不想 Material 和 shader 在一个 AB 包里的话,又要生成足够的变体,还有一种靠谱的方法就是 SVC,基于篇幅这个就不介绍了

参考文档:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/112959
推荐阅读
相关标签
  

闽ICP备14008679号