Unity AR FoundationをEditor上でシミュレーションする方法

iOS13が公開されたことで、beta版をインストールしなくてもUnityでARKit3の新規のHuman Segmentaionなどが使えるようになりました。対応端末はiPhone X以降です。

実験としてこのようなアプリを作ってみました。

が、開発中、実機で確認するのがとても面倒でした。ARKitはiPhone搭載のカメラのセンサを使うため、実機じゃないと動きません。毎回Unityからビルド→Xcodeプロジェクトに書き出し→実機で確認。のステップを踏まなくてはいけません。毎回実機確認に5分くらいかかります。ARKit2まではARKit Remote for Unityで、Unity Editorとつなげてデバックができました。とところが、ARKit3の機能に対応しら新しいUnityライブラリである、AR Foundation (動作時点ではver 3.0.0-preview.3)ではリモートデバックができないようです。公式スレッドによれば開発中だけど難航しているようです。

なにかを作るときにはツールから作るのをモットーにしているので、Unity Editorにリモートで送信できるツールを作ってみました。現在はHuman Segmentation関係の機能しか用意していませんが、普通に動きそうです。

ソースコードはこちら。

github.com

仕組みはシンプルで、iPhoneからARに必要な情報を送ります。

  • NDI
    • RGBカメラ(YCbCr色空間) 映像
    • Depth 映像
    • Human Segmentation 映像
  • WebSocket
    • カメラmatrixとかの情報

NDIはWi-Fi経由だと遅いのですが、USB経由で接続すると0.3秒くらいの遅延で収まりました。

Hack C#でprivateプロパティだけなstructを作る

シミュレータを作るときに困った点を一つ。ARFoundationはコア部分はネイティブC++プラグイン。そちらで生成したstructをやり取りしているので、C#から見えるstructはプロパティが全部プライベートでした。

[StructLayout(LayoutKind.Sequential)]
public struct XRCameraFrame
{
    public long timestampNs
    {
        get { return m_TimestampNs; }
    }
    long m_TimestampNs;

    public Matrix4x4 projectionMatrix
    {
        get { return m_ProjectionMatrix; }
    }
    Matrix4x4 m_ProjectionMatrix;
    
    ...
}

↑このようなクラス構造。Unityでもunsafe{}を使えばこのようなstructでも作れますが、ライブラリ化を考えるとなるべくunsafe以外の方法でstructを作ったほうが良いため、以下の様な共用体をつくることで解決しました。c#で共用体って作れるんですね…。

[StructLayout(LayoutKind.Sequential)]
public struct MyCameraFrame
{
    public long timestampNs;
    public Matrix4x4 projectionMatrix;
    ...
}

[StructLayout(LayoutKind.Explicit)]
public struct CameraFrameUnion
{
    [FieldOffset(0)] public MyCameraFrame a;
    [FieldOffset(0)] public XRCameraFrame b;
}

void main()
{
    var union = new CameraFrameUnion()
    {
         a = new MyCameraFrame()
         {
               // これはいじれる!
               timestampeNS = 1000,
         }
    }
    
    // 共用体なので、aを作るとbもできる!
    XRCameraFrame b = union.b;
}