LoginSignup
2

More than 1 year has passed since last update.

posted at

updated at

【Unity】Graphics.DrawMeshInstancedを使う

Graphics.DrawMeshInstanced

Draws the same mesh multiple times using GPU instancing.

GPU instancingを利用して同一メッシュのインスタンスを複数回描画できる。

  • 各インスタンスのTransformation情報はMatrix4x4の配列で指定する。
  • MaterialPropertyBlockでインスタンスごとのデータを設定できる。
  • 一度に描画できるインスタンス数は1023個まで。
  • マテリアルのMaterial.enableInstancingをtrueに設定しておく必要がある。

実際に試す

メッシュにQuadを指定、GPU Instancingに対応したマテリアルを用意して以下のコードを実行する。
(マテリアルに使用したシェーダーは後述。)

TestDrawMeshInstanced01.cs
using UnityEngine;

namespace TestGPUInstancing
{
    public class TestDrawMeshInstanced01 : MonoBehaviour
    {
        [SerializeField] private Mesh _mesh;
        [SerializeField] private Material _material;
        
        private const int MeshCount = 1023;
        private Matrix4x4[] _matrices;
        
        private void Start()
        {
            _matrices = new Matrix4x4[MeshCount];
            for (int i = 0; i < MeshCount; i++)
            {
                var pos = new Vector3(
                    UnityEngine.Random.Range(-10f, 10f),
                    UnityEngine.Random.Range(-10f, 10f),
                    UnityEngine.Random.Range(-10f, 10f)
                );

                _matrices[i] = Matrix4x4.TRS(pos, Quaternion.identity, Vector3.one);
            }
        }

        private void Update()
        {
            Graphics.DrawMeshInstanced(_mesh, 0, _material, _matrices);
        }
    }
}

結果は以下の通り。
Batchesが5と非常に少なくなっており、バッチが効いていることがわかる。

スクリーンショット 2022-03-22 15.09.03.png

インスタンス毎に色を変える

上述の通りMaterialPropertyBlockを用いてインスタンス毎にプロパティを設定できる。
色を設定するためにSetColorとうい関数が用意されているが、これだと全インスタンスが同一カラーになってしまう。インスタンス毎に色を指定したい場合は、SetVectorArrayを用いる。

TestDrawMeshInstanced02.cs
using UnityEngine;

namespace TestGPUInstancing
{
    public class TestDrawMeshInstanced02 : MonoBehaviour
    {
        [SerializeField] private Mesh _mesh;
        [SerializeField] private Material _material;
        
        private const int MeshCount = 1023;
        private Matrix4x4[] _matrices;
        // MaterialPropertyBlockを使用してインスタンス毎にプロパティを設定できる
        private MaterialPropertyBlock _propertyBlock;
        
        private void Start()
        {
            _matrices = new Matrix4x4[MeshCount];
            _propertyBlock = new MaterialPropertyBlock();

            var colors = new Vector4[MeshCount];
            for (int i = 0; i < MeshCount; i++)
            {
                var pos = new Vector3(
                    UnityEngine.Random.Range(-10f, 10f),
                    UnityEngine.Random.Range(-10f, 10f),
                    UnityEngine.Random.Range(-10f, 10f)
                );

                _matrices[i] = Matrix4x4.TRS(pos, Quaternion.identity, Vector3.one);
                var r = i / (float)MeshCount;
                var g = 1f - i / (float)MeshCount;
                colors[i] = new Vector4(r, g, 0f, 1f);
            }
            _propertyBlock.SetVectorArray("_Color", colors);
        }

        private void Update()
        {
            Graphics.DrawMeshInstanced(_mesh, 0, _material, _matrices, MeshCount, _propertyBlock);
        }
    }
}

結果。
スクリーンショット 2022-03-22 16.03.34.png

1024個以上のインスタンスを描画する。

上述の通り、Graphics.DrawMeshInstancedで一度に描画できるインスタンス数は1023個が上限。それ以上のインスタンスを描画する場合は自前でバッチすればOK。

TestDrawMeshInstanced03.cs
using System.Collections.Generic;
using UnityEngine;

namespace TestGPUInstancing
{
    public class TestDrawMeshInstanced03 : MonoBehaviour
    {
        [SerializeField] private Mesh _mesh;
        [SerializeField] private Material _material;

        private int MeshCount = 1023 * 4;
        private List<Matrix4x4[]> _batches;
        
        private void Start()
        {
            _batches = new List<Matrix4x4[]>();
            var matrices = new Matrix4x4[1023];

            for (int i = 0; i < MeshCount; i++)
            {
                if (i % 1023 == 0)
                {
                    matrices = new Matrix4x4[1023];
                    _batches.Add(matrices);
                }

                var pos = new Vector3(
                    UnityEngine.Random.Range(-10f, 10f),
                    UnityEngine.Random.Range(-10f, 10f),
                    UnityEngine.Random.Range(-10f, 10f)
                );

                matrices[i % 1023] = Matrix4x4.TRS(pos, Quaternion.identity, Vector3.one);
            }
        }

        private void Update()
        {
            foreach (var batch in _batches)
            {
                Graphics.DrawMeshInstanced(_mesh, 0, _material, batch, 1023);
            }
        }
    }
}

結果。

スクリーンショット 2022-03-22 16.40.12.png

使用したShader

ShaderはGPU Instancingに対応させる必要がある。この辺は凹みさんの記事を参考。

.shader
Shader "Unlit/GPUInstanceTest"
{
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
 
        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
            };
 
            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);
 
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }
       
            fixed4 frag(v2f i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i);
                return UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
            }
            ENDCG
        }
    }
}

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2