【unity】材质修改使用实例

Material

Set
  • render.material
    会实例化一份材质
    runtime 允许多怪
    API render.material.Get/SetXXX
  • shareMaterial
    会全都改
    非运行时不能创建.material
    API render.shareMaterial.Get/SetXXX
  • MaterialPropertyBlock
    直接set,多MPB之间独立,但Keyword依然从material设置
    降低内存使用
    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
  1. shader keyword
    需要额外的
    Material.EnableKeyword/DisableKeyword(string)
  2. 消耗查看(非真机)
    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后,结合项目实际情况去进行相应的修改。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GPU实例化是一种在GPU上生成和渲染大量相似物体的技术。在Unity中,GPU实例化可以用于大规模的场景渲染,例如草地、树木、岩石等自然物体。 GPU实例化的实现方式是使用一个网格和一个材质,然后通过修改网格的变换矩阵来渲染大量相似的物体。在Unity中,可以通过以下步骤来使用GPU实例化: 1. 创建一个网格对象,包含一个或多个子网格。 2. 创建一个材质对象,并将其设置为支持GPU实例化。 3. 在脚本中,使用Graphics.DrawMeshInstanced来渲染实例化的网格对象。 4. 在渲染之前,需要将实例化的属性发送给GPU,例如变换矩阵、颜色等。 例如,以下代码演示如何在Unity使用GPU实例化: ```csharp public class InstancedObject : MonoBehaviour { public Mesh mesh; public Material material; public int instanceCount; private Matrix4x4[] matrices; private Vector4[] colors; private void Start() { matrices = new Matrix4x4[instanceCount]; colors = new Vector4[instanceCount]; for (int i = 0; i < instanceCount; i++) { matrices[i] = Matrix4x4.TRS(Random.insideUnitSphere * 10f, Quaternion.identity, Vector3.one); colors[i] = new Vector4(Random.value, Random.value, Random.value, 1f); } } private void Update() { for (int i = 0; i < instanceCount; i++) { matrices[i] = Matrix4x4.TRS(Random.insideUnitSphere * 10f, Quaternion.identity, Vector3.one); colors[i] = new Vector4(Random.value, Random.value, Random.value, 1f); } } private void OnRenderObject() { material.SetVectorArray("_Color", colors); Graphics.DrawMeshInstanced(mesh, 0, material, matrices, instanceCount); } } ``` 在这个例子中,我们创建了一个InstancedObject类,并在Start方法中初始化了变换矩阵和颜色数组。在Update方法中,我们每帧更新变换矩阵和颜色数组,以模拟物体的运动。在OnRenderObject方法中,我们将变换矩阵和颜色数组发送给GPU,并使用Graphics.DrawMeshInstanced方法渲染了实例化的网格对象。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值