Material
Set
- render.material
会实例化一份材质
runtime 允许多怪
API render.material.Get/SetXXX - shareMaterial
会全都改
非运行时不能创建.material
API render.shareMaterial.Get/SetXXX - MaterialPropertyBlock
直接set,多MPB之间独立,但Keyword依然从material设置,MPB本来就可以每个不一样,但是keyword不可以
降低内存使用
Editor使用此接口将使面板不可修改
Get
var renderers = gameObject.GetComponentsInChildren<Renderer>();
var _propID = Shader.PropertyToID("_Color");
var mpbs = new List<MaterialPropertyBlock>();
foreach (var r in renderers)
{
//Material.Get()
Debug.LogError("Material.Get():" + r.sharedMaterial.GetColor(_dataPropID));
//MPB
var mpb = new MaterialPropertyBlock();
r.GetPropertyBlock(mpb);
mpbs.Add(mpb);
Debug.LogError("GetMPB:" + mpb.GetColor(_dataPropID));//如果没有修改不会Log
//Debug.LogError("GetXXXFromMPB:" + r.GetFloatFromMPB(_dataPropID));
}
GC Test
var renderers = gameObject.GetComponentsInChildren<Renderer>();
UnityEngine.Profiling.Profiler.BeginSample("Get sharedMaterials");
var s = renderers[0].sharedMaterials;//gc
UnityEngine.Profiling.Profiler.EndSample();
UnityEngine.Profiling.Profiler.BeginSample("Get shader.name");
var c = s[0].shader.name;//gc
UnityEngine.Profiling.Profiler.EndSample();
renderers[i].GetFloatFromMPB(_dataPropID);//无gc
选择什么API修改材质
Editor:直接在Hierarchy上拖动材质参数相当于修改Project本地文件
Clone:如果用AnimationK动画进行修改,会创建出一个实例出来
ShareMaterial:使用 ShareMaterial会将全局的材质都修改,但不会保存到本地
所以此时,需要根据用到的情况去选择使用的API
其他
Keyword
通俗地说,Shader中的关键字就是一个个标签,方便材质在渲染时绑定不同的Shader变体,实现不同的效果。我们可以在Shader片段中使用编译指令(compile directives)来定义Shader关键字。从变体生成特点上可分为“multi_compile”和“shader_feature”两类,从作用范围角度可分为局部关键字和全局关键字。
需要说明的是,shader_feature 预编译指令行至少有两个关键字。如果只定义了一个关键字KW_X,则会默认生成一个下划线关键字。以下两行指令等价:
一般Shader片段中multi_compile类关键字每增加一个,或者启用的shader_feature类关键字增加一个,该Shader的变体数量就会增加一份。而对于变体数与内存、显存的关系,UWA曾做过以下实验:
使用#pragma multi_compile定义的一行关键字为一组,每组包含两个关键字,对产生的内存进行统计,结果如下:
由此可见变体数和ShaderLab的内存占用基本成正比。而由于没有使用Shader进行渲染,GfxDriver内存不会增加,没有参与渲染的Shader变体是不会经历CreateGPUProgram传入GfxDriver内存中的。
shader
- shader keyword
需要额外的Material.EnableKeyword/DisableKeyword(string)
- 消耗查看(非真机)
ShareMaterial 0.001ms
SetXXX 0.001ms
SetKeyword 0.001ms
SVC
- 使用流程收集(multi_compile会一定生成) → 1.预生成 → 2.预热
- 这个Shader会被编译成两个变体:一是只包含A模块代码的变体A;二是只包含B模块代码的变体B;
- 指定的第一个关键字是默认生效的,即默认使用变体A;在脚本里用Material.EnableKeyword或Shader.EnableKeyword来控制运行时具体使用变体A还是变体B;
- 它们声明的Keyword是全局的,可以对全局的包含该Keyword的不同Shader起作用;全局最多只能声明256个这样的Keyword;
- )请注意Keyword的数量和变体的数量之间的关系,并可能由此导致的性能开销,比如声明#pragma multi_compile A B和#pragma multi_compile D E 这样的两组Keyword会产生 2x2=4 个Shader变体,但若声明10组这样的keyword,则该Shader会产生1024个变体
(5)区别!!!!特别注意!!!!: - 如果使用shader_feature,build时没有用到的变体会被删除,不会打出来。也就是说,在build以后环境里,运行代码Material.EnableKeyword(“B”)可能不起作用,因为没有Material在使用变体B,所以变体B没有被build出来,运行时也找不到变体B
对Unity项目而言,Shader变体有其存在的积极意义。除了代码的共用与运行时渲染效果的动态改变之外,还增加了Shader程序在GPU上的执行效率。
对GPU来说,处理类似于“if-else”结构的分支语句不是它的强项,GPU的特点和功能决定了它更适合去并列地“执行”重复性的任务,而不是去“选择”。所以Shader变体的存在就很好地解决了这个问题,GPU只需要根据关键字去执行对应的Variant内容就可以,避免了性能下降的可能。同时,项目在运行时,可以通过在代码中选择不同的Shader变体,从而动态地改变着色器功能。
但是Shader变体是一把双刃剑。在带来以上便利的同时,也存在着各种问题:
1)在Build阶段,过多的Shader变体数量会使得Build耗时明显上升,而最终的项目包体体积也会变得臃肿。
2)在项目运行阶段,Shader变体会以其庞大的数量产生可观的内存占用,同时也会导致项目加载时间的增加,也就是俗说的“卡顿”。
所以本条规则会扫描项目中的Shader脚本,根据项目中Material上开启的关键字情况去计算可能生成的变体数。开发团队可以在找出这些可能生成过多变体数的Shader后,结合项目实际情况去进行相应的修改。