现代基于光栅化的批处理技术一般有动态批处理,静态批处理,gpuinstance,srpbatch等。
其中我要说的是gpuinstance,gpuinstance可以让同一个mesh,同一个material,来传递多个统一的数据结构到buff中。进行后面的着色等处理。
其中数据结构是我们所关心的,因为我们可能需要创建不同的位置,旋转或物体颜色,法线等信息。
gpuinstance使用比较简单,就是在shader中有#pragma multi_compile_instancing就好,那么在unity中就会在对应的材质下出现
通常来说只要同一mesh,同一material就能合并到一个drawcall。
修改gpuinstance下的材质的属性有两种方式
第一种是直接修改位置和旋转信息,这两块不会引起gpuinstance的切换。但是缩放会,如果缩放不一样的话会放到另一个drawcall里。这是因为缩放会改变顶点间的距离,所以mesh自然就不一样了。另外我们还要注意如果多个material,一定要排序好,让相同的material在同一个render queue。这样才能让相同的material在同一个队列下合并。不然会打乱合并顺序。
下面给出unity的排序的先后顺序
第二种是修改材质的颜色等内部属性。这块如果直接用material.setcolor等方式去做是不生效的,要不就新创建了一个material让他到了另一个drawcall,要不就是sharedmaterial让全部相同的material都修改了。
但我们的目的不是这个,我们的目的是每个不同的物体,能修改他们的颜色等信息但又能合并一个drawcall。
这时就要用到MaterialPropertyBlock,他是材质修改属性的工具,不会产生新的材质。
MaterialPropertyBlock props = new MaterialPropertyBlock();
props.SetColor("_BaseColor", new Color(0,0,0,1));
GameObject.Find("Cube").GetComponent<MeshRenderer>().SetPropertyBlock(props);
但是如果我们直接这样修改,也不会生效,这时会出现一个错误
non-instanced properties set for instanced shader
这是因为instance不识别我们的属性,我们需要把他包入一个UNITY_INSTANCING_BUFFER_START中,然后用UNITY_DEFINE_INSTANCED_PROP来定义这个属性。当然我们还是需要在顶点和片元着色器中定义instance的idUNITY_SETUP_INSTANCE_ID(input);
这里有好几个宏,这些宏都可以再UnityInstancing.hlsl中找到定义。
下面给出解释:
#pragma multi_compile_instancing
“multi_compile_instancing”会使你的Shader生成两个Variant,其中一个定义了Shader关键字INSTANCING_ON,另外一个没有定义此关键字。 除了这个#pragma指令,下面所列其他的修改都是使用了在UnityInstancing.cginc里定义的宏(此cginc文件位于Unity_Install_Dir\Editor\Data\CGIncludes)。取决于关键字INSTANCING_ON是否被定义,这些宏将展开为不同的代码。
UNITY_INSTANCE_ID
用于在Vertex Shader输入 / 输出结构中定义一个语义为SV_InstanceID的元素。 UNITY_INSTANCING_CBUFFER_START(name) /
UNITY_INSTANCING_CBUFFER_END
每个Instance独有的属性必须定义在一个遵循特殊命名规则的Constant Buffer中。使用这对宏来定义这些Constant Buffer。“name”参数可以是任意字符串。
UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
定义一个具有特定类型和名字的每个Instance独有的Shader属性。这个宏实际会定义一个Uniform数组。
UNITY_SETUP_INSTANCE_ID(v)
这个宏必须在Vertex Shader的最开始调用,如果你需要在Fragment Shader里访问Instanced属性,则需要在Fragment Shader的开始也用一下。这个宏的目的在于让Instance ID在Shader函数里也能够被访问到。 UNITY_TRANSFER_INSTANCE_ID(v, o)
在Vertex Shader中把Instance ID从输入结构拷贝至输出结构中。只有当你需要在Fragment Shader中访问每个Instance独有的属性时才需要写这个宏。
UNITY_ACCESS_INSTANCED_PROP(_Color)
访问每个Instance独有的属性。这个宏会使用Instance ID作为索引到Uniform数组中去取当前Instance对应的数据。
如果要访问对应的属性就需要在定义属性的时候加类似下面的定义
UNITY_INSTANCING_BUFFER_START(STTest)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_INSTANCING_BUFFER_END(STTest)
STTest是结构体名字,float4是属性类型,_BaseColor是具体的属性名。
然后在使用的地方
float4 newColor = UNITY_ACCESS_INSTANCED_PROP(STTest, _BaseColor);
这样定义后就直接用newColor 代替_BaseColor。这样就能让instance识别到具体的属性了,也就能合并批次了。