GPU instancing: 对同一网格,同时渲染多个副本时使用,底层调用的是多实例渲染接口,例如OpenGL的glDrawArraysInstanced接口。GPU实例对于绘制场景中多次出现的几何图形(例如,树或灌木丛)非常有用。首先使用GPU Instance,需要材质着色器支持 GPU 实例化,接着就可以在 Project 窗口中选择材质,最后在 Inspector 中勾选 Enable Instancing 复选框。
特点:
使用GPU Instance的限制条件:
Unity 自动选取要实例化的网格渲染器组件和 Graphics.DrawMesh 调用。请注意,不支持 SkinnedMeshRenderer(骨骼蒙皮渲染)。
Unity 仅在单个 GPU实例化绘制调用中,批量处理那些共享相同网格和相同材质的游戏对象。使用少量网格和材质可以提高实例化效率。要创建变体,请修改着色器脚本为每个实例添加数据,下述shaderlab代码为官方示例代码。
还可以使用 Graphics.DrawMeshInstanced 和 Graphics.DrawMeshInstancedIndirect 调用来通过脚本执行 GPU 实例化。
使用实例化渲染实例代码
Shader "Custom/InstancedColorSurfaceShader" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// 基于物理的标准光照模型,并对所有光照类型启用阴影
#pragma surface surf Standard fullforwardshadows
// 使用 Shader Model 3.0 目标
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color)
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
然后可以在C#脚本中给对应的对象设置Color属性
MaterialPropertyBlock props = new MaterialPropertyBlock();
MeshRenderer renderer;
foreach (GameObject obj in objects)
{
float r = Random.Range(0.0f, 1.0f);
float g = Random.Range(0.0f, 1.0f);
float b = Random.Range(0.0f, 1.0f);
props.SetColor("_Color", new Color(r, g, b));
renderer = obj.GetComponent<MeshRenderer>();
renderer.SetPropertyBlock(props);
}
更多关于Unity GPU Instance的内容,请点击这里查看官方文档
在SRP Batcher中,GameObject的内置引擎属性(transform等)是有专门的提交路径(下图实线箭头所示),和材质的提交是分开的(下图虚线所示),这样做的好处是,我们每帧都更新一些必要的属性,例如位置,大小等信息,而材质就可以以增量改变的方式提交给GPU,而恰好材质的提交(渲染状态的改变)是非常印象效率的,在SRP Batcher管线中,每一种材质在GPU内存中都有一个CBuffer存放对应的参数,只要材质的参数没有发生变化,那么在每一帧中就不必从CPU提交材质到GPU。从而减少CPU的消耗,提升渲染效率,官网上的示意图如下:
在上述的合批中,多数都是要求使用相同的材质(meterail),而贴图也是属于材质的一种属性,如果两个材质仅仅贴图不一样,这也会导致材质不一样,就不印象合批,所以把多张贴图打包为图集,这样就可以是的材质引用同一份贴图,使得合批得以进行。
SRP Batcher和static batching可以共存,如果一个GameObject使用了static batching,Unity就会禁用GPU instancing ,即使GameObject使用的是GPU instancing Shader; 如果一个GameObject使用了GPU instancing , Unity就会禁用Dynamic batching;
Stencil 状态
Stencil 状态即模板测试,通过模板缓冲来实现特定的效果,在 Unity 中,Mask 组件就是通过该功能实现,一个 Mask 组件及其控制的渲染节点,需要至少三次 Draw call。第一次开启模板测试并调用一次 Draw call,刷新模板缓冲。第二次绘制对需要通过模板测试的区域进行设置。第三次再进行实际的子节点内容绘制,绘制结束再关闭模板测试。因此使用 Mask 组件就无法与其他相邻节点进行批次处理,但是 Mask 组件内部的连续节点在满足合并规则的情况下还是会进行合批。
Stencil 使用的最佳实践
如果界面内使用大量 Mask 组件会带来 Draw call 的剧增,因此应该尽量减少 Mask 组件的使用。如有使用 Mask 组件的节点,应该尽量不要穿插在连续并且可以进行批次合并的节点层级内,这样也可以尽量规避 Mask 打断本可以合并批次的一系列连续节点。