今天在看别人写的shader的时候,看到了一个shader中有好几个 #pragma multi_compile ,之前也没有很系统的研究过这一块的内容,今天详细的做一下这一块的研究。
多重编译的作用: 对于通用代码可以进行公用,在使用设置了不同关键字的时候运行不同的代码段。
定义:
variants(变体):在启用不同关键字时候组合成的shader程序称之为变体。
uber shaders(超级着色器):拥有大量变体的着色器,例如Unity自带的 Standard Shader

着色器变体的创建:

#pragma multi_compile
 #pragma multi_compile_local
 #pragma shader_feature
 #pragma shader_feature_local


上面带local的表明定义的是局部关键字
shader_feature和shader_compile的 唯一区别 :feature在打包(build)的时候不会把未使用的 variants 打进去, 因此应该使用shader_feature来声明那些material设置的关键词,全局代码使用的关键词最好用shader_compile来声明

关键字的设置:
Shader.EnableKeyword:启用全局关键字
Shader.DisableKeyword:禁用全局关键字
CommandBuffer.EnableShaderKeyword:使用CommandBuffer来启用全局关键字
CommandBuffer.DisableShaderKeyword:使用CommandBuffer可以禁用全局关键字
Material.EnableKeyword:为常规着色器启用本地关键字
Material.DisableKeyword:禁用常规着色器的本地关键字
ComputeShader.EnableKeyword:为计算着色器启用本地关键字
ComputeShader.DisableKeyword:禁用计算着色器的本地关键字

Shader.EnableKeyword ("METHOD1");
Shader.DisableKeyword ("METHOD2");

关于计算着色器详看下面文章,写得十分不错,因为现在还没有到要用计算着色器的地方,等以后要用到我也会整理一篇相关的文章
计算着色器相关教学链接

着色器变体的工作方式:
以 #pragma multi_compile 为例,
在 shader中 声明了
1、#pragma multi_compile METHOD1 METHOD2
该指令生成了两个着色器变体 METHOD1 和 METHOD2,在运行的时候如果 设置了关键字 激活了其中一个变体则使用激活的变体,如果均未设置关键字则默认使用第一个 METHOD1
**2、#pragma multi_compile __ METHOD1 **
该指令生成了两个着色器变体,一个未定义的“__”,和一个“METHOD1 ”,如此可以避免把两个关键字都用完,因为一个项目中关键字的数量是有限的,详看后文

着色器变体的数量计算:

#pragma multi_compile A B C
 #pragma multi_compile D E

如上将生成 3*2个着色器变体

如下图操作点击 show 可以查看所有着色器变体

Unity xlua编译cjson unity自带编译器_着色器


使用ShaderVariantCollection是生成指定变体:

好处: ShaderVariantCollection是unity5.x以后用来记录shader的哪些变体需要被生成。这样做的好处就是在shader_feature与multi_compile结合使用时,能够设置生成何种变体,从而避免生成不必要的变体;shader不必和material打在一个包中,避免了多个包中存在相同的变体资源;明确直观的显示了哪些变体是需要生成的。

生成ShaderVariantCollection:

1、通过Create->Shader-> Shader Variant Collection,就可以新建一个shader variant collection文件,如下图,可以自己选择变体

Unity xlua编译cjson unity自带编译器_shader_02


2、让Unity帮我们自动生成,点击“save to asstets”按钮,生成unity帮我们自动收集的,使用到的变种信息,我们可以通过打开场景让Unity收集各个场景中使用到的shader信息

Unity xlua编译cjson unity自带编译器_#pragma_03


ShaderVariantCollection预加载:

1、将ShaderVariantCollection添加到预加载中,如下图

Unity xlua编译cjson unity自带编译器_预加载_04


2、在ShaderVariantCollection加载完之后调用

ShaderVariantCollection shaderVariantCollection = Resources.Load <ShaderVariantCollection>( "MainShaderVariant");
if (shaderVariantCollection )
      shaderVariantCollection.WarmUp ();

关键字的限制:
Unity中全局关键字最多不超过256个,其中Unity内部已经使用了约60个,因此要注意在定义关键字的时候不要超过限制
当我们使用shader_feature和multi_compile的时候都会增加全局关键字的数量,为此我们可以声明本地关键字,即使用shader_feature_local和multi_compile_local
本地关键字,即
①只在该shader中使用;
②本地关键字和全局关键字冲突的时候优先使用本地关键字;
③本地关键字在每个shader中的数量不超过64个;
④如果在material中设置了不存在的本地关键字,那么Unity会自动生成一个全局关键字

Unity的内置变体的快捷方式:
multi_compile_fwdbase 编译PassType.ForwardBase所需的所有变体。这些变体处理不同的光照贴图类型,并且启用或禁用了主方向光的阴影。
multi_compile_fwdadd 编译PassType.ForwardAdd的变体。这将编译变体以处理定向,聚光或点光源类型,以及带有cookie纹理的变体。
multi_compile_fwdadd_fullshadows 与相同multi_compile_fwdadd,但还包括灯光具有实时阴影的功能。
multi_compile_fog 扩展为多种变体以处理不同的雾类型(关闭/线性/ exp / exp2)。

大多数内置快捷方式会产生许多着色器变体。如果您知道项目不需要它们,则可以使用 #pragma skip_variants 跳过它们中的一些。例如:

#pragma multi_compile_fwdadd
#pragma skip_variants POINT POINT_COOKIE

Graphics tiers and shader variants(图形层和着色器变体):
Unity将检查GPU的功能,并确定其对应的图形层,我们可以为每一个图层创建一组着色器变体,并通过“#pragma hardware_tier_variants renderer(其中renderer是有效的渲染平台)” 设置不同的图形层 (此功能仅与内置渲染管线兼容),例如

#pragma hardware_tier_variants gles3

Unity xlua编译cjson unity自带编译器_Unity xlua编译cjson_05


除其他关键字外,Unity还会为每个着色器生成三个着色器变体,如下图

UNITY_HARDWARE_TIER1  
//第一图形层(低)-对应于着色器定义UNITY_HARDWARE_TIER1。
//选择此级别的对象是:-不支持OpenGL ES 3的Android设备-iPhone 5、5C和更早的版本-iPod Touch第5代或更早版本-iPad 4或更早版本-iPad Mini第1代-台式机上的DirectX 9类硬件-HoloLens
UNITY_HARDWARE_TIER2
//第二个图形层(中)-对应于着色器定义UNITY_HARDWARE_TIER2。
//选择此等级的用户包括:-支持OpenGL ES 3.0+的Android设备-iPhone 5S及更高版本-iPod Touch第6代-iPad Air及更高版本-iPad Mini第2代及更高版本-Apple TV
UNITY_HARDWARE_TIER3
//第三图形层(高)-对应于着色器定义UNITY_HARDWARE_TIER3。
//选择此层可用于:-台式机上的OpenGL或DirectX 11+类硬件-Mac上的Metal

我们可以在如下图位置设置

Unity xlua编译cjson unity自带编译器_Unity xlua编译cjson_06


此功能仅与内置渲染管线兼容。它与通用渲染管道(URP),高清渲染管道(HDRP)或自定义脚本渲染管道不兼容

变体数量过多的优化方案 ↓
优化着色器变体