Optimization Graphics.DrawMeshInstanced of Unity3D Research Institute

If GPU Instancing is used in the project, many people will use the underlying API Graphics.DrawMeshInstanced to draw in Update.

Let's do an extreme test first, as shown in the following code.

 void Update()
    {
        for (int i = 0; i < 1024; i++)
        {
            Graphics.DrawMeshInstanced(mesh, 0, material, m_atrix4x4s, 1023);
        }
    }

As shown in the figure below, it can be seen that each frame of Update has a relatively high time-consuming.

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;
   
}

 

If the position matrix and MaterialPropertyBlock parameters of the Graphics.DrawMeshInstanced call in each frame are the same, I wonder if it can be optimized.

In fact, CommandBuffer also has a DrawMeshInstanced method, so you don't need to call it every frame in Update. When the position matrix or MaterialPropertyBlock parameters change, DrawMeshInstanced is called and placed in the CommandBuffer for rendering. As shown in the figure below, using CommandBuffer.DrawMeshInstanced only refreshes when the position of the element changes, which is obviously much more efficient.


Combining actual projects, such as grass, stones, and fences on the ground that have been made before, once they are created, they will not change their positions. It is more suitable to use commandbuffer. Of course, cropping needs to be done by yourself, GPU instancing does not support automatic cropping

Guess you like

Origin blog.csdn.net/mango9126/article/details/110367724