Unity GPU Instancing的使用尝试
似乎是在Unity5.4中开始支持GPU Instacing,但如果要比较好的使用推荐用unity5.6版本,因为这几个版本一直在改。
这里测试也是使用unity5.6.2进行测试
在5.6的版本里,Instancing不再是一个单独的shader,而是一个开关。
如果使用Instancing功能需要开启,否则调用相关接口时会报错

默认情况下,多个一样的模型会被动态批次合并优化掉,动态批次合并有很多种情况不可用,其中一种就是镜像的情况。
这里用镜像后的实例模型和GPU Instancing做比较
GPU Instancing大致代码如下(用Graphics一次性调用也减少了层级对象的创建开销):
void Update() { var meshRenderer = template.GetComponent(); var meshFilter = template.GetComponent (); 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其实也有一些限制的,比如不支持蒙皮网格等
这些支持信息在官网的页面都有罗列 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,在非dx平台上兼容不佳或者不支持。
然后再测一下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这种结构。
另外我用我的红米3S测了下,不支持InstancedIndirect。硬件方面需要sm4.0以上,移动平台应该是openGL ES4 ?
这个要等unity2017了。

