Note: You can only draw a maximum of 1023 instances at once.
ドキュメントにもあるように Graphics.DrawMeshInstanced
で一度に生成できるインスタンス数の上限は 1023 となっており、1023を超える数を生成すると以下のエラーが表示される
ArgumentOutOfRangeException: Count must be in the range of 0 to 1023.
スクリプトサンプル
Graphics.DrawMeshInstanced で渡すインスタンス数は自前で管理する必要があるため、以下のような実装が必要になる
using System.Collections.Generic; using UnityEngine; public class GrassRenderer : MonoBehaviour { [SerializeField] private Material _material; [SerializeField] private Mesh _mesh; [SerializeField] private float _range = 5; [SerializeField] private int _amount = 1000; private List<Matrix4x4[]> _transforms; private int instanceMax = 1023; private void Start() { var drawCount = Mathf.CeilToInt(_amount / (float) 1023); _transforms = new List<Matrix4x4[]>(); for (var i = 0; i < drawCount; i++) { var length = i == drawCount - 1 ? _amount % instanceMax : instanceMax; var m = new Matrix4x4[length]; for (var j = 0; j < m.Length; j++) { var x = Random.Range(-_range, _range); var z = Random.Range(-_range, _range); var position = new Vector3(x, 0f, z); var rotate = Quaternion.Euler(new Vector3(0f, Random.Range(0f, 360f), 0f)); m[j] = Matrix4x4.TRS(position, rotate, Vector3.one); } _transforms.Add(m); } } private void Update() { for (var i = 0; i < _transforms.Count; i++) { Graphics.DrawMeshInstanced(_mesh, 0, _material, _transforms[i], _transforms[i].Length); } } }
Unity2021
Unity2021では同じ機能の拡張である Graphics.RenderMeshInstanced
が追加されている
public static unsafe void RenderMeshInstanced<T>( in RenderParams rparams, Mesh mesh, int submeshIndex, T[] instanceData, [DefaultValue("-1")] int instanceCount = -1, [DefaultValue("0")] int startInstance = 0) where T : unmanaged
そのため、上記のように自前で複数リストに分ける必要が無いため、先程のスクリプトは以下のように書き換えられる
using UnityEngine; public class GrassRenderer : MonoBehaviour { [SerializeField] private Material _material; [SerializeField] private Mesh _mesh; [SerializeField] private float _range = 5; [SerializeField] private int _amount = 1000; /// <summary> /// 描画用パラメータのバッファ /// </summary> private GraphicsBuffer _indirectBuf; /// <summary> /// Mesh 描画の設定 /// </summary> private GraphicsBuffer.IndirectDrawIndexedArgs[] _commandData; private RenderParams _renderParams; private Matrix4x4[] _transforms = new Matrix4x4[10]; private int instanceMax = 1023; private void Start() { _renderParams = new RenderParams(_material) { worldBounds = new Bounds(Vector3.zero, 10000 * Vector3.one), matProps = new MaterialPropertyBlock() }; _transforms = new Matrix4x4[10]; for (var i = 0; i < _transforms.Length; i++) { var x = Random.Range(-_range, _range); var z = Random.Range(-_range, _range); var position = new Vector3(x, 0f, z); var rotate = Quaternion.Euler(new Vector3(0f, Random.Range(0f, 360f), 0f)); _transforms[i] = Matrix4x4.TRS(position, rotate, Vector3.one); } _commandData = new GraphicsBuffer.IndirectDrawIndexedArgs[1]; _commandData[0].indexCountPerInstance = _mesh.GetIndexCount(0); _commandData[0].baseVertexIndex = _mesh.GetBaseVertex(0); _commandData[0].startIndex = _mesh.GetIndexStart(0); _commandData[0].instanceCount = (uint)_transforms.Length; _indirectBuf = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 1, GraphicsBuffer.IndirectDrawIndexedArgs.size); _indirectBuf.SetData(_commandData); } private void Update() { var drawCount = Mathf.CeilToInt(_amount / (float) instanceMax); for (var i = 0; i < drawCount; i++) { var length = i == drawCount - 1 ? _amount % instanceMax : instanceMax; Graphics.RenderMeshInstanced(_renderParams, _mesh, 0, _transforms, length, i * instanceMax); } } private void OnDestroy() { _indirectBuf?.Dispose(); _indirectBuf = null; } }