【Unity】GPU Instancingで大量に描画したMeshをImmersalの位置合わせ処理に適用する

## サンプル
まずはサンプルです。円形の机にピッタリ芝生が生えるように配置しました。
芝生はGPU Instancingで大量に描画したMeshです。

https://youtube.com/shorts/tKBCbPyl4MU?si=xVN2cUJH_CQpORIl


## 前提
Immersalの位置合わせはARSpace.csをアタッチしたオブジェクト配下にAR用オブジェクトを配置することで実現できます。

内部では事前に作成したマップデータの特徴点を元に、ARSpaceが移動/回転することで位置合わせが行われています。

では、GPU Instancingで大量に描画したMeshについても、"ARSpace.csをアタッチしたオブジェクト配下"に並べておけば可能なのか、というとそうではありません。

GPU Instancingで大量にMeshを描画するにはGraphicsのAPIを利用します。
その際、MeshにはMatrix4x4で座標/回転/スケールの情報を与えることになります。

詳細は以下記事に実例とともにまとめています。

【参考リンク】：[そろそろShaderをやるパート91 -URP編- GPU Instancingで大量に草を生やす](https://zenn.dev/kento_o/articles/4b9166e1ed5fba)

まとめると、単純に"ARSpace.csをアタッチしたオブジェクト配下"に並べた場合、以下GIFのように移動/回転させることができません。

![GPUInstancingWithImmersal.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/867d7989-5f2e-7b4c-83d4-3764506b7efd.gif)

## 実装方法
実装方法としてはMeshのMatrix4x4にARScaceのTransformが反映されるようにしてあげればOKです。

以下コードです。

```CS
using System.Collections.Generic;
using Immersal.AR;
using UnityEngine;
using Random = UnityEngine.Random;

namespace TestGPUInstancing
{
    public class DrawMeshInstancing : MonoBehaviour
    {
        [SerializeField] private ARLocalizer _arLocalizer;
        [SerializeField] private Mesh _mesh;
        [SerializeField] private Material _material;
        [SerializeField] private float _radius = 5.0f;
        [SerializeField] private int _meshCount = 512;

        private Matrix4x4[] _matrices;
        private List<Vector3> _positions;

        private void Start()
        {
            _matrices = new Matrix4x4[_meshCount];
            _positions = GeneratePointsInCircle(_meshCount, _radius);
            _arLocalizer.OnPoseFound += OnPoseFound;
            UpdatePositions();
        }

        private void Update()
        {
            //デバッグ用。
            if (Application.isEditor && transform.hasChanged)
            {
                UpdatePositions();
                transform.hasChanged = false;
            }

            Graphics.DrawMeshInstanced(_mesh, 0, _material, _matrices, _meshCount);
        }

        private void OnDestroy()
        {
            _arLocalizer.OnPoseFound -= OnPoseFound;
        }

        private void OnPoseFound(LocalizerPose pose)
        {
            UpdatePositions();
        }

        private void UpdatePositions()
        {
            for (var i = 0; i < _meshCount; i++)
            {
                var meshPosition = transform.rotation * _positions[i] + transform.position;
                _matrices[i] = Matrix4x4.TRS(meshPosition, transform.rotation, transform.localScale);
            }
        }

        private List<Vector3> GeneratePointsInCircle(int numberOfPoints, float radius)
        {
            var points = new List<Vector3>();

            for (var i = 0; i < numberOfPoints; i++)
            {
                var randomPointOnCircle = Random.insideUnitCircle * radius;
                points.Add(new Vector3(randomPointOnCircle.x, 0f, randomPointOnCircle.y));
            }

            return points;
        }
    }
}
```

以下画像のような階層構造にしておけば、ARSpaceが移動すれば子のARObject
も移動するので、DrawMeshInstancing.csをアタッチしたオブジェクト自身のTransformを利用できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/ddfae409-83e5-e025-b31b-3c82112bf769.png)

以下GIFのようにARSpaceの移動はもちろん、Unity Editor上での細かい位置合わせも可能となります。
![ImmersalWithGPUInstancing.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/c5879893-bfb9-3eaf-bcde-6f984531e5f2.gif)

## 宣伝
以下イベントで同様の手法を用いてMRグラス上で大量のオブジェクトを出現させるという試みにチャレンジさせていただきました。テレビでも取り上げられ、大盛況だったとのことです。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">「人の記憶」パートの作り込みにご協力いただきました。<br>スマホの限られたリソースで実現する<br>ARグラスを使ったGPUインスタンシングの事例！<br>流石のクオリティです！ <a href="https://t.co/JH0yKP7QGX">https://t.co/JH0yKP7QGX</a> <a href="https://t.co/QvfP136U0y">pic.twitter.com/QvfP136U0y</a></p>&mdash; daisuketaki@U. (@YUUU__jp) <a href="https://twitter.com/YUUU__jp/status/1724460761802846232?ref_src=twsrc%5Etfw">November 14, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

AR/VR関連でお困りごとあればご相談ください。