【Unity】MaterialPropertyBlock(MPB) GPUInstancing SRPBatch个人见解

121 篇文章 2 订阅
43 篇文章 28 订阅

参考:Unity MaterialPropertyBlock 正确用法(解决无法合批等问题)_costa100的专栏-CSDN博客

Instancing and Material Property Blocks | Ronja's tutorials

Unity SRP Batcher的工作原理_zakerhero的博客-CSDN博客

 SRPBatcher&GPUInstancing_汤姆猫X的博客-CSDN博客

 一、MaterialPropertyBlock(MPB) :属性块,用法:

MaterialPropertyBlock mpb = new MaterialPropertyBlock();//最好全局唯一静态对象

Render(MeshRenderer、SpriteRenderer).GetPropertyBlock(mpb);

mpb.SetFloat(...)//类似material.SetFloat使用

Render(MeshRenderer、SpriteRenderer).SetPropertyBlock(mpb);

这种使用方法与直接material区别是不会产生新的材质实例对象,而是会产生一个新的存储区域在CPU,它配合GPU Instancing使用,因为GPU Instancing要求同网格同材质,而且不支持SRPBatch(凡是使用MPB的则不可能生效SRPBatch,理由是SRPBatch必须所改动属性都在GPU的CBuffer中,不然会打破Batch)

二、GPU Instancing:GPU实例,条件:同网格(mesh)同材质实例(即一个网格一个材质实例),它可以批处理所有相同网格相同材质实例的物体,其中shader伪代码如下:(加粗部分是重点)
Pass{

CGPROGRAM
...
#pragma multi_compile_instancing

顶点输入结构体
{
...
UNITY_VERTEX_INPUT_INSTANCE_ID
}
顶点输出结构体(片段着色器输入结构体)
{
...
UNITY_VERTEX_INPUT_INSTANCE_ID
}
UNITY_INSTANCING_BUFFER_START(Props) //Props是BUFFER块名称访问时使用到
        //属性块里的属性全部类似写到这里
        UNITY_DEFINE_INSTANCED_PROP(类型, 变量名)
        UNITY_DEFINE_INSTANCED_PROP(类型, 变量名)
UNITY_INSTANCING_BUFFER_END(Props)

顶点着色器函数(v)
{
        输出结构体 o;
        UNITY_SETUP_INSTANCE_ID(v);
        UNITY_TRANSFER_INSTANCE_ID(v, o);

}
片段着色器函数(i)
{
        UNITY_SETUP_INSTANCE_ID(i);
        类型 value = UNITY_ACCESS_INSTANCED_PROP(Props, 变量名); //访问Props块的变量(即MPB传递过来的变量)

}

三、SRPBatch:可编程渲染管线批处理,条件:URP、HDRP、自定义RP环境下,能加速CPU处理同shader且相同变体的物体(即使不同材质或不同材质实例都可以加速),加速的是CPU准备设置属性的时间,因属性都会被直接存储在GPU的CBuffer中,如果材质的属性没有变化,就不会进行去花费时间设置属性,从而节省DrawCall时间,但DrawCall数量是不变的。
注意:不能使用MaterialPropertyBlock去设置属性,因为MPB会设置的属性产生新的存储空间存储而不是存储到GPU的CBuffer中,从而打断SRPBatch。

需引入:#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/UnityInput.hlsl"
同理所有CGPROGRAM ENDCG改成HLSLPROGRAM ENDHLSL并且sampler2D改TEXTURE2D(_MainTex),额外增加一个采样器SAMPLER(sampler_MainTex),采样使用half4 color = _MainTex.Sample(sampler_MainTex, uv);
重点是将所有Properties块内的属性全部放到
CBUFFER_START(UnityPerMaterial)
        float4 xxx;
CBUFFER_END
在Shader界面能看见SRP Batch compiled就代表会支持SRP Batch了。
(注意:贴图属性是无法进入CBuffer处理的,它不用写到CBUFFER_START,但这不影响SRP正常加速,因为SRP不需要同材质,除非shader变体不一样,GPU Instancing同理也是不能写入UNITY_INSTANCING_BUFFER_START的!SRPBatch就和往常一样进行设置材质贴图即可,GPU Instancing因要求同材质,所以使用图集Atlas才能保证贴图一致进行合批,如果你不使用图集,那么一张贴图就是相当于生成一个新的材质实例,会打断合批!关于使用图集后会产生的UV问题已经在本文说明)

SRPBatch补充知识:
1.使用Animation动画组件使用动画去控制材质属性会打断SRPBatch,因为Animation是使用MPB形式修改材质属性的!
2.SRPBatch不支持SpriteRenderer,只能用MeshRenderer或SkinnedMeshRenderer,而且因为SpriteRenderer是自动根据贴图生成网格的,所以GPU Instancing也不支持SpriteRenderer,因为它要相同网格!所以要自己做一个自动生成网格的脚本并且做一个共享网格池,如果相同大小的则使用池子里的!因为不使用SpriteRenderer了所以会有一个遮罩功能还需要我们实现,遮罩是模板测试实现的,配合SpriteMask组件使用,SpriteMask会给所在区域的范围增加模板值1,也就是如果原始模板值是0(一般都是只要你没有改),它就会变成1,所以你只要在shader里写一个模板测试,在SpriteMask范围内可见就是要 模板值为1,用Equals比较,Keep,不可见则是NoEquals很简单实现起来。对应上   //
    // 摘要:
    //     This enum controls the mode under which the sprite will interact with the masking
    //     system.
    public enum SpriteMaskInteraction
    {
        //
        // 摘要:
        //     The sprite will not interact with the masking system.
        None = 0,  不开启模板测试 将比较设置为 Always通过即可
        //
        // 摘要:
        //     The sprite will be visible only in areas where a mask is present.
        VisibleInsideMask = 1,  将比较设置为:Equal
        //
        // 摘要:
        //     The sprite will be visible only in areas where no mask is present.
        VisibleOutsideMask = 2  将比较设置为 NotEqual
    }

没有测试过有多个SpriteMask情况(自测)

GPU Instancing补充知识:

1、不支持SkinnedMeshRenderer,支持MeshRenderer,SpriteRenderer能支持但不好做,因网格不一致会打断合批,SpriteRenderer是动态生成的网格根据贴图来生成的,除非贴图大小一致。

2、GPU Instancing要求材质一致,所以设置材质时使用sharedMaterial设置,一定不要使用material。

(及其重要)*3、设置材质属性时,instanced属性用MPB设置,非instanced属性使用sharedMaterial.SetXxx设置,据我所知非instanced属性是材质纹理,最常见的就是_MainTex主纹理,你只能使用sharedMaterial.SetTexture进行设置,因为sharedMaterial是共享材质,如果你有不同的纹理贴图需要设置,则你要自己管理好一个静态共享sharedMaterial字典,根据贴图id或名称去获取相应的sharedMaterial。一般来说是使用图集贴图去设置,但图集贴图的话显示出来会是一张很大的整图,而不是小图片的话就是UV问题了,这个在另一个文章说明到:【Shader】图集UV错乱问题&支持MeshRenderer显示图集图片_两水先木示的博客-CSDN博客

若你尝试使用MPB设置材质非instanced数据,会出现non-instanced字样的提示 无法合批!

无法合批情况:

GPU Instance合批失败,实例存在不同网格打断了合批,这是我尝试使用SpriteRenderer进行GPUInstance的结果,根本无法进行合批。 

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值