今天在看别人写的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 可以查看所有着色器变体
使用ShaderVariantCollection是生成指定变体:
好处: ShaderVariantCollection是unity5.x以后用来记录shader的哪些变体需要被生成。这样做的好处就是在shader_feature与multi_compile结合使用时,能够设置生成何种变体,从而避免生成不必要的变体;shader不必和material打在一个包中,避免了多个包中存在相同的变体资源;明确直观的显示了哪些变体是需要生成的。
生成ShaderVariantCollection:
1、通过Create->Shader-> Shader Variant Collection,就可以新建一个shader variant collection文件,如下图,可以自己选择变体
2、让Unity帮我们自动生成,点击“save to asstets”按钮,生成unity帮我们自动收集的,使用到的变种信息,我们可以通过打开场景让Unity收集各个场景中使用到的shader信息
ShaderVariantCollection预加载:
1、将ShaderVariantCollection添加到预加载中,如下图
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还会为每个着色器生成三个着色器变体,如下图
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
我们可以在如下图位置设置
此功能仅与内置渲染管线兼容。它与通用渲染管道(URP),高清渲染管道(HDRP)或自定义脚本渲染管道不兼容
变体数量过多的优化方案 ↓
优化着色器变体