似乎是在Unity5.4中開始支援GPU Instacing,但如果要比較好的使用推薦用unity5.6版本,因為這幾個版本一直在改。
這裡測試也是使用unity5.6.2進行測試
在5.6的版本裡,Instancing不再是一個單獨的shader,而是一個開關。
如果使用Instancing功能需要開啟,否則調用相關接口時會報錯

預設情況下,多個一樣的模型會被動态批次合并優化掉,動态批次合并有很多種情況不可用,其中一種就是鏡像的情況。
這裡用鏡像後的執行個體模型和GPU Instancing做比較
注意,在Unity5.6.2或者之後的版本中,隻要材質球勾選Instancing,即自動開啟并使用GPU Instancing。
GPU Instancing大緻代碼如下(用Graphics一次性調用減少了層級對象的建立開銷):
void Update()
{
var meshRenderer = template.GetComponent<MeshRenderer>();
var meshFilter = template.GetComponent<MeshFilter>();
var mesh = meshFilter.sharedMesh;
var matrices = new Matrix4x4[instanceCount];
for (int i = 0; i < matrices.Length; i++)
{
var position = Random.insideUnitSphere * range;
var rotation = Quaternion.LookRotation(Random.insideUnitSphere);
var scale = Vector3.one * Random.Range(-2f, 2f);
var matrix = Matrix4x4.TRS(position, rotation, scale);
matrices[i] = matrix;
}
Graphics.DrawMeshInstanced(mesh, 0, meshRenderer.sharedMaterial, matrices);
}
是實時随機的位置,會看見隻有13個Batches.
正常執行個體化測試腳本(挂了正弦運動腳本,注意鏡像反轉,使其無法動态批次合并):
for (int i = 0; i < instanceCount; i++)
{
var instancedTemplate = Instantiate(template);
instancedTemplate.transform.position = Random.insideUnitSphere * range;
instancedTemplate.transform.forward = Random.insideUnitSphere;
instancedTemplate.transform.localScale = Vector3.one * Random.Range(-2f, 2f);
}
大概在1020個Batches
另外我還打了個APK包測了下,居然能在我的紅米3S上跑。這就有點厲害了
那麼GPU Instacing其實也有一些限制的,比如不支援蒙皮網格等(不過資源商店有一個Animation Instacing: 連結)
(補充:Unity官方開源了一個Animation Instacing: https://blogs.unity3d.com/cn/2018/04/16/animation-instancing-instancing-for-skinnedmeshrenderer/)
這些支援資訊在官網的頁面都有羅列 https://docs.unity3d.com/Manual/GPUInstancing.html
硬體需求:
GPU Instancing is available on the following platforms and APIs:
- DirectX 11 and DirectX 12 on Windows
- OpenGL Core 4.1+/ES3.0+ on Windows, macOS, Linux, iOS and Android
- Metal on macOS and iOS
- Vulkan on Windows and Android
- PlayStation 4 and Xbox One
- WebGL (requires WebGL 2.0 API)
子產品間的需求(沒找到原版的文章,翻譯版摘抄一段):
下列情況不能使用Instancing:
- 使用Lightmap的物體
- 受不同Light Probe / Reflection Probe影響的物體
- 使用包含多個Pass的Shader的物體,隻有第一個Pass可以Instancing前向渲染時,受多個光源影響的物體隻有Base Pass可以instancing,Add Passes不行
另外,預設的DrawMeshInstanced有1024執行個體數的限制
需要DrawMeshInstancedIndirect,而這個接口依賴ComputerShader,一些平台不支援。
然後再測一下GPU Instanced Indirect,也就是DrawMeshInstancedIndirect這個接口
似乎是借助ComputerShader實作超過1024數量的Instancing
下圖為3萬個Cube:
代碼和shader我做了點修改,大緻如下:
void Update()
{
// Update starting position buffer
if (cachedInstanceCount != instanceCount) UpdateBuffers();
for (int i = 0; i < mPositions.Length; i++)
{
float angle = Random.Range(0.0f, Mathf.PI * 2.0f);
float distance = Random.Range(20.0f, 100.0f);
float height = Random.Range(-2.0f, 2.0f);
float size = Random.Range(0.05f, 0.25f);
mPositions[i] = new Vector4(Mathf.Sin(angle) * distance, height, Mathf.Cos(angle) * distance, size);
}
positionBuffer.SetData(mPositions);
instanceMaterial.SetBuffer("positionBuffer", positionBuffer);
// Render
Graphics.DrawMeshInstancedIndirect(
instanceMesh,
0,
instanceMaterial,
new Bounds(Vector3.zero, new Vector3(100.0f, 100.0f, 100.0f)),
argsBuffer);
}
shader需要額外定制,這點比較蛋疼。如果換成standard讀不到positionBuffer這種結構。
DrawMeshInstancedIndirect的具體使用案例,可以參考這兩個連結:
https://docs.unity3d.com/ScriptReference/Graphics.DrawMeshInstancedIndirect.html
https://github.com/tiiago11/Unity-InstancedIndirectExamples
補充測試工程位址: https://gitee.com/Hont/GPUInstancingTest
(unity2017.4)
擴充閱讀:
Geometry instancing
https://en.wikipedia.org/wiki/Geometry_instancing
Unity中的批處理優化與GPU Instancing
http://forum.china.unity3d.com/thread-22714-1-3.html