赞
踩
multi_complie 和 shader_feature 编译指令往往用于正式游戏项目的优化
multi_complie 的用法:
#pragma multi_compile NAMEA, NAMEB, NAMEC, …,
参考代码:
- Shader "Jaihk662/ShaderVariantTest"
- {
- Properties
- {
- _MainTex("Texture", 2D) = "white" {}
- //枚举宏,注意在预编译指令中,宏要大写
- [KeywordEnum(R,G,B)] _Color("Color", float) = 0
- }
- SubShader
- {
- Tags { "RenderType" = "Opaque" }
- LOD 200
-
- Pass
- {
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #pragma multi_compile _COLOR_R _COLOR_G _COLOR_B
- #pragma multi_compile __ DB_ON
- #include "UnityCG.cginc"
-
- struct v2f
- {
- float4 vertex: SV_POSITION;
- float2 uv: TEXCOORD0;
- };
-
- sampler2D _MainTex;
- float4 _MainTex_ST;
-
- v2f vert(appdata_base v)
- {
- v2f o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
- return o;
- }
-
- fixed4 frag(v2f i): SV_Target
- {
- #if DB_ON
- return fixed4(1, 1, 1, 1);
- #elif _COLOR_R
- return fixed4(1, 0, 0, 1);
- #elif _COLOR_G
- return fixed4(0, 1, 0, 1);
- #elif _COLOR_B
- return fixed4(0, 0, 1, 1);
- #else
- fixed4 color = tex2D(_MainTex, i.uv);
- return color;
- #endif
- }
- ENDCG
- }
- }
- }
-
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- public class TestShader: MonoBehaviour
- {
- public Material mat;
- void Update()
- {
- if (Input.GetKey(KeyCode.Q))
- mat.EnableKeyword("DB_ON");
- if (Input.GetKey(KeyCode.W))
- mat.DisableKeyword("DB_ON");
- }
- }
其中 NAMEA 为枚举名,一个非空的枚举名为一个全局的关键字(Keyword)
关于关键字(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 个变体
上面的代码中有下面这么一段,那么对于这第二行,__ 是什么东西呢?
- #pragma multi_compile _COLOR_R _COLOR_G _COLOR_B
- #pragma multi_compile __ DB_ON
- #pragma shader_feature FANCY_STUFF
其实它代表着当前这一行预编译指令中,所有的关键字都不生效,可以理解为“空”,即未定义宏(NoKeyword),对于 #pragma multi_compile __ DB_ON,其 Shader 仍会编译成两个变体:一是不包含 DB_ON 模块代码的变体;二是包含 DB_ON 模块代码的变体,当然默认为__,即不包含 DB_ON 的变体生效
局部的关键字:
同样能使用 #pragma multi_complie_local 的方法声明只在对应 shader 内部生效的关键字(keyword),对于局部的关键字(keyword):
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,基于篇幅这个就不介绍了
参考文档:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。