如果项目中使用GPU Instancing的话,很多人都会用底层API Graphics.DrawMeshInstanced在Update中绘制。
我们先来做个极端的测试,如下代码所示。
1 2 3 4 5 6 7 8 9 |
void Update() { for (int i = 0; i < 1024; i++) { Graphics.DrawMeshInstanced(mesh, 0, material, m_atrix4x4s, 1023); } } |
如下图所示,能看出来在Update每帧都有一个比较高的耗时。
如果每帧的调用Graphics.DrawMeshInstanced的位置矩阵和MaterialPropertyBlock参数都是一致,我就在想能否进行优化。
其实CommandBuffer也有DrawMeshInstanced方法,这样就可以不用在Update里每帧调用了。当位置矩阵或者MaterialPropertyBlock参数发生变化时在调用DrawMeshInstanced,放入CommandBuffer中渲染。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
using UnityEngine; using UnityEngine.Rendering; public class G1 : MonoBehaviour { //GPU instancing材质 public Material material; //GPU instancing网格 public Mesh mesh; //随便找个位置做随机 public Transform target; //是否使用cammandBuffer渲染 public bool useCommandBuffer = false; //观察摄像机 public Camera m_Camera; private Matrix4x4[] m_atrix4x4s = new Matrix4x4[1023]; void Start() { CommandBufferForDrawMeshInstanced(); } private void OnGUI() { if (GUILayout.Button("<size=50>当位置发生变化时候在更新</size>")) { CommandBufferForDrawMeshInstanced(); } } void Update() { if (!useCommandBuffer) { GraphicsForDrawMeshInstanced(); } } void SetPos() { for (int i = 0; i < m_atrix4x4s.Length; i++) { target.position = Random.onUnitSphere * 10f; m_atrix4x4s[i] = target.localToWorldMatrix; } } void GraphicsForDrawMeshInstanced() { if (!useCommandBuffer) { SetPos(); Graphics.DrawMeshInstanced(mesh, 0, material, m_atrix4x4s, m_atrix4x4s.Length); } } void CommandBufferForDrawMeshInstanced() { if (useCommandBuffer) { SetPos(); if (m_buff != null) { m_Camera.RemoveCommandBuffer(CameraEvent.AfterForwardOpaque, m_buff); CommandBufferPool.Release(m_buff); } m_buff = CommandBufferPool.Get("DrawMeshInstanced"); for (int i = 0; i < 1; i++) { m_buff.DrawMeshInstanced(mesh, 0, material, 0, m_atrix4x4s, m_atrix4x4s.Length); } m_Camera.AddCommandBuffer(CameraEvent.AfterForwardOpaque, m_buff); } } CommandBuffer m_buff = null; } |
如下图所示,使用CommandBuffer.DrawMeshInstanced只有当元素位置改变时在刷新,这样明显效率高了很多。
结合实际项目,比如之前做过的地面的草、石头、栅栏一旦创建出来以后都不会去改变位置,更适合用commandbuffer来做。当然裁剪还需要自己来做,GPU instancing不支持自动裁剪。
- 本文固定链接: https://www.xuanyusong.com/archives/4683
- 转载请注明: 雨松MOMO 于 雨松MOMO程序研究院 发表
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!
想问下 CommandBuffer但是不支持设置layer
不符合要求的layer 不就传入 Matrix4x4[] matrices, 就可以了
极端测试那个是画了1024*1023个?画一次1023个的时候graphic和commandbuffer性能不会差很大吧
你好雨松,想问下怎么跟Graphics.DrawMeshInstanced绘制出来的物体做交互,比如碰撞,改变颜色等,能否提供一个思路,谢啦!
改变颜色很简单, 可以参考我之前的那篇 Graphics.DrawMeshInstanced lightmap的文章,
碰撞可以搭配ECS JOB 过几天我会写这方面的文章。
你好,有例子工程可以下载研究么?
你好雨松,自己怎么做裁剪呢?
场景可以分格子,不过我建议配合Job system