GPU Instancing 概述:
常见的静态合批和动态合批都是在 CPU 端做优化,而 GPU Instancing 则是在 GPU 端做优化。其核心思想是,用一个 Draw Call 让 GPU 渲染一个物体有时候很浪费,不如在合适的时机,用一个 Draw Call 让 GPU 渲染一堆物体(100个,200个,甚至更多)。
GPU Instancing 的工作原理是将具有相同 Mesh 和相同 Material 的多个 Object 的绘制放在同一个 Draw Call 中。GPU Instancing 在提高渲染建筑、树、草地等使用相同的材质和网格的对象的效率方面效果显著。虽然 GPU Instancing 只能把具有相同的 Mesh 和 Material 的对象放到同一个 Draw Call 中渲染,但是每个实例可以有不同的材质参数(例如 color,scale)。使用 GPU Instancing 可以显著提高渲染性能。
具体来说使用 GPU Instancing,Unity 会将可见范围内的所有符合要求的 Object 的对象的属性(位置,uv等)放入到 GPU 中的缓冲区中,从中抽取一个对象作为实例送入渲染流程,这个对象包含了所有符合要求对象的公共信息(那些它们相同的部分),收到 Draw Call 之后,从显存中取出实例的部分共享信息与从GPU常量缓冲器中取出对应对象的相关信息一并传递到下一渲染阶段。
如何使用GPU Instancing:
-Vertex and Fragment shader:
这是一个简简单单的 Vertex and Fragment shader:
Shader "Custom/GPUInstancingSupport"
{
Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
float4 _Color;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
}
ENDCG
}
}
}
为了支持 GPU Instancing,首先要在 Shader 中添加一条预编译指令:
#pragma multi_compile_instancing
然后要在 appdata 的定义中添加这行代码:
struct appdata
{
float4 vertex : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
在 v2f 的定义中添加这行代码:
struct v2f
{
float4 vertex : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID // necessary only if you want to access instanced properties in fragment Shader.
};
变量的定义也需要修改,显式地告诉 Unity 这个 Instance property 被声明在一个指定 Buffer 中:
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
UNITY_INSTANCING_BUFFER_END(Props)
在 vert 和 frag 函数中做如下修改:
v2f vert(appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o); // necessary only if you want to access instanced properties in the fragment Shader.
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i); // necessary only if any instanced properties are going to be accessed in the fragment Shader.
return UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
}
部分语句的添加与否看注释。
修改后代码如下:
Shader "Custom/GPUInstancingSupport"
{
Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID // necessary only if you want to access instanced properties in fragment Shader.
};
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
UNITY_INSTANCING_BUFFER_END(Props)
v2f vert(appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o); // necessary only if you want to access instanced properties in the fragment Shader.
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i); // necessary only if any instanced properties are going to be accessed in the fragment Shader.
return UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
}
ENDCG
}
}
}
GPU Instancing使用效果:
我们将上述 Shader 赋值给新建的 Material,可以在 Inspector 中观察到有一个新的 toggle:Enable GPU Instancing。
当你的 Shader 支持 GPU Instancing 时,会添加这个 toggle,我们打开它就能启用 GPU Instancing。
场景中有 5 种材质,均打开 GPU Instancing,15个 Sphere 时:
虽然 Sphere 数量有 15 个,但我们可以发现 Draw Call 数量仅仅为 6,其中有一个是用于画 skybox 的,因此渲染 15 个 Sphere 一共只需要 5 个 Draw Call,这就是 GPU Instancing 的力量,大幅减少了 Draw Call。
而如果我们关闭 GPU Instancing:
此外,GPU Instancing 能在同一 Draw Call 中提交的物体数量是有限的,这是由 GPU Constant Buffer 大小限制导致的。
当场景中有 500 个 Sphere 时,只用一个 Draw Call 就可以渲染完成:
当场景中有 600 个 Sphere 时,需要两个 Draw Call:
使用 MaterialPropertyBlock:
在上文中,我们提到 GPU Instancing 支持在一个 Draw Call 里渲染具有相同 Mesh 相同 Material 的 Object,但 Material 的材质属性可以不一样,比如 color,scale 等。但不同于常规的使用 Render 的 material 属性直接设置材质属性,这种方式会在运行时生成新的 Material,我们需要使用 MaterialPropertyBlock 来设置材质属性。
(参考 MaterialPropertyBlock_阿赵的博客-CSDN博客 )
参考:
- 静态批处理、动态批处理、GPU Instancing [原创]静态批处理、动态批处理、GPU Instancing
http://newhappy.com.cn/index.php/2020/05/14/batch/
- U3D优化批处理-GPU Instancing了解一下 U3D优化批处理-GPU Instancing了解一下 - 知乎
https://zhuanlan.zhihu.com/p/34499251
- Unity Documentation Manual: GPU Instancing Unity - Manual: GPU instancing
https://docs.unity3d.com/2019.4/Documentation/Manual/GPUInstancing.html
- MaterialPropertyBlock MaterialPropertyBlock_阿赵的博客-CSDN博客
https://blog.csdn.net/liweizhao/article/details/81937590