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

一、关键字与Shader变体

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 200Pass{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_ONreturn fixed4(1, 1, 1, 1);#elif _COLOR_Rreturn fixed4(1, 0, 0, 1);#elif _COLOR_Greturn fixed4(0, 1, 0, 1);#elif _COLOR_Breturn fixed4(0, 0, 1, 1);#elsefixed4 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)

  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 个变体

二、特殊的双下划线与关键字限制

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

#pragma multi_compile _COLOR_R _COLOR_G _COLOR_B
#pragma multi_compile __ DB_ON
#pragma shader_feature FANCY_STUFF

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

  • 全局的 Keyword 只能有256个!因此可以尽量使用 __,这样可以省掉一个关键字
  • 对于只有一个关键字的情况,它相当于是省略了前面的 __,是一种简便写法

局部的关键字:

同样能使用 #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 项目时没有用到的变体就会被删除不会打出来。这也意味着在非编辑器中运行代码 Material.EnableKeyword("B") 可能会不起作用,因为没有 Material 在使用变体 B,变体 B 没有被 build 出来,运行时也找不到变体 B,而在编辑器环境下,multi_complie 和 shader_feature 没有区别

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

如果偏要使用 shader_feature,但又避免不了用到所有的变体,也可以通过把对应 Shader 加入到 “always included shaders” 中,这样它所有的变体都会被直接打包到游戏

参考文档:

  • https://docs.unity3d.com/cn/current/Manual/SL-MultipleProgramVariants.html
  • https://blogs.unity3d.com/cn/2018/05/14/stripping-scriptable-shader-variants/
  • https://docs.unity3d.com/cn/current/Manual/OptimizingShaderLoadTime.html

UnityShader30:预编译指令multi_complie和shader_feature相关推荐

  1. C#中的预编译指令介绍

    原文:C#中的预编译指令介绍 1.#define和#undef 用法: #define DEBUG #undef DEBUG #define告诉编译器,我定义了一个DEBUG的一个符号,他类似一个变量 ...

  2. 几个预编译指令的用法

    *.几个预编译指令的用法 #        字符串化运算符,其主要效果是把参数的名字转换为字符串. Example: //1. *.h中定义 #defineSTRINGLIZE(ivalue)  #i ...

  3. 预编译指令与相关宏小结

    //======================================================================== //TITLE: //    预编译指令与相关宏小 ...

  4. c语言中条件编译相关的预编译指令

     一. 内容概述 本文主要介绍c语言中条件编译相关的预编译指令,包括#define.#undef.#ifdef.#ifndef.#if.#elif.#else.#endif.defined. 二. ...

  5. 在Silverlight 和WPF中使用预编译指令 if..else..endif (译)

    下面的是中文翻译,有些扯淡的话就略过了,想看原文,请到这里 对于预编译指令,作者分成了几篇文章来讲解的.鉴于翻译后的文字较少,我把作者的几篇合为了一篇.下面进入正题. 一.总揽: 想要编写特定平台的代 ...

  6. C语言的预编译,程序员必须懂的知识!【预编译指令】【预编译过程】

    由"源代码"到"可执行文件"的过程包括四个步骤:预编译.编译.汇编.链接.所以,首先就应该清楚的首要问题就是:预编译只是对程序的文本起作用,换句话说就是,预编译 ...

  7. C#基础13:预编译指令

    PS:注释和讲解全在代码中 1. 预编译指令 预编译指令也叫预处理指令,在程序正式编译之前执行 这些指令不会转化为可执行代码中的命令,但是会影响编译过程的各个方面 具体例子如代码: using Sys ...

  8. C/C++常用预编译指令介绍

    目录 1.#include指令 2.#define和#undef指令 3.#ifdef.#ifndef.#else.#elif和#endif指令 4.#error指令 5.编译器预置宏__FILE__ ...

  9. c语言的预编译指令是什么,c语言预编译指令有哪些?

    c语言预编译指令有哪些? 预处理器的主要作用就是把通过预处理的内建功能对一个资源进行等价替换,最常见的预处理器指令有:文件包含.条件编译.布局控制和宏替换4种. 文件包含 #include是一种最为常 ...

最新文章

  1. 基于线段的激光雷达和单目联合曲面重建​
  2. 13个JavaScript图表图形绘制插件
  3. 3.5.3 CSMA协议
  4. 复制链接到safari浏览器打开_JS实现复制到剪贴板(兼容FF/Chrome/Safari所有浏览器)...
  5. Android地图 总于实现了!
  6. 暴君第一季/全集Tyrant迅雷下载
  7. 【uni-app】swiper 实现纵向轮播,且支持鼠标滚轮滚动翻页
  8. 2020江苏计算机二级考试官网,2020年秋季江苏省计算机等级考试报名通知
  9. 谈谈重载(overload)覆盖(override)与隐藏
  10. python字符串相关习题
  11. 最全NFC芯片技术厂商介绍及应用介绍
  12. python mypy类型检查_Python中类型检查的详细介绍
  13. 读后感Java多线程编程核心技术十二
  14. (转)关于同步和异步的理解(ajax网络编程)
  15. Sqlserver 索引
  16. 和秋叶一起学PPT:又快又好打造说服力幻灯片
  17. 一阶惯性环节的性能分析——自动控制原理基础补充(二)
  18. 戴尔R720服务器U盘安装Windows
  19. 【已解决】The server cannot or will not process the request due to something that is perceived to be ...
  20. 楪祈机器人_饥荒 Inori楪祈人物MOD V20161211

热门文章

  1. python干嘛用的-学 Python 都用来干嘛的?
  2. python读音-原来Python应该这么念,怪不得总被嘲笑~
  3. python和java选择哪个-python和Java选择哪一个?
  4. python下载-【Python下载 官方版】Python 3.8.0-ZOL软件下载
  5. python必备入门代码-20行python代码的入门级小游戏的详解
  6. router-LInk传参与接受参数(记录)
  7. mysql 5.6 bug_MySQL 5.6的一个bug引发的故障
  8. JS的Document类型
  9. 计算机网络重点知识总结 谢希仁版,计算机网络谢希仁版网络层知识点总结
  10. 【java笔记】接口的定义,接口的使用