Table of Contents

session の実行結果を取得する

session の実行中には、シーン内の一部オブジェクトの transform やカメラ画像などが変更されます。場合によっては、これらの変更だけではアプリの要件を満たせず、session の各 frame の実行結果を取得して、そのデータをさらに処理する必要があります。この記事では、これらの結果データの取得方法と使い方を説明します。

はじめる前に

InputFrame の更新を取得する

InputFrameUpdate イベントを使って InputFrame の更新を取得できます。このイベントは、session の各 frame の出力データ内で InputFrame が変化したときのみ発火します。

注記

InputFrameUpdate は、EasyAR が画面描画を行う session でのみ有効です。一般に、AR Foundation やヘッドセットを使用している場合は無効で、その場合はそれらのサードパーティライブラリが提供する方法でデータ更新を取得する必要があります。

InputFrame を使うと、物理カメラ画像、カメラパラメータ、タイムスタンプ、物理カメラのワールド座標系に対する transform、トラッキング状態などを取得できます。ただし、カメラ transform はすでに session によって仮想カメラや他のオブジェクトへ適用されているため、通常は InputFrame からカメラ transform を取得する必要はありません。

現在 frame の物理カメラ画像を取得する

InputFrame.image() メソッドを使って、Image 型の物理カメラ画像データを取得できます。

たとえば、次のコードは InputFrame の更新時に物理カメラ画像を取得します。

Session.InputFrameUpdate += (inputFrame) => {
    using (var image = inputFrame.image())
    {
    }
};
注意

Image 型のデータと、そこから取得した他の class 型データを使うときは、Dispose() が正しく呼ばれるようにしてください(上のコードの using 文がそれを保証します)。そうしないと、メモリリークや画面更新停止などが発生する可能性があります。

InputFrameImage を次の frame まで保持して使う場合は、保持するデータ量に応じて ARAssembly.ExtraBufferCapacity の値を増やす必要があります。そうしないと、バッファ不足でデータ取得に失敗することがあります。

InputFrame を保持したい場合は、Clone() で参照コピーを作成し、不要になったらそのコピーに対して Dispose() を呼ぶ必要もあります。

物理カメラの frame rate は通常 rendering frame rate より低いため、すべての rendering frame で InputFrameUpdate イベントを受け取れるわけではありませんが、同様に物理カメラ映像もすべての rendering frame で更新されるわけではありません。次に InputFrameUpdate が発火するまでのすべての rendering frame の画面内容は、現在の InputFrame の画像と一致します。

注記

InputFrame の画像は必ず現在 frame の仮想カメラ背景画面と一致しますが、背景描画時に拡大縮小や切り取りが行われることがあるため、取得した画像サイズや比率が画面表示と一致しないのは正常です。

また、InputFrame.image() が返す画像データは CPU で読めるデータであり、GPU texture ではありません。GPU 上で画像データを使いたい場合は、画像データを GPU texture にアップロードするか、CameraImageRenderer.RequestTargetTexture(Action<Camera, RenderTexture>) インターフェースから直接 GPU texture を取得してください。

[任意] 物理カメラ画像の描画を横取りする

ARAssembly.CameraImageRenderer を使って物理カメラ画像の描画を制御できます。

次のコードは物理カメラ画像の描画を停止します。

if (Session.Assembly != null && Session.Assembly.CameraImageRenderer.OnSome)
{
    Session.Assembly.CameraImageRenderer.Value.enabled = false;
}

ここでは、まず ARAssembly.CameraImageRenderer が存在するか確認する必要がある点に注意してください。

注記

上記の方法で画面更新を停止できるのは、EasyAR が画面描画を行う session だけです。一般に、AR Foundation やヘッドセットを使用している場合は無効で、その場合はそれらのサードパーティライブラリが提供する方法を使って同等の機能を実現する必要があります。

物理カメラ画像の描画を停止した後、アプリケーションは InputFrame を通じて物理カメラ画像データを取得し、それを使って独自描画を行えます。

transform の更新を取得する

PostSessionUpdate イベントを通じて、session の各更新後にシーン内オブジェクトの transform データを取得できます。

注記

Mega など一部の機能では、画像に変化がなくサービス更新の明示的な要求がなくても、AR 計算は各 rendering frame で実行されています。そのため、すべての transform 変化を取得したい場合は、特定の frame だけでなく毎 frame transform データを取得する必要があります。

仮想カメラの transform を取得する

ARAssembly.Camera を通じて、シーン内のカメラの transform を取得できます。

Session.PostSessionUpdate += () =>
{
    var position = Session.Assembly.Camera.transform.position;
    var rotation = Session.Assembly.Camera.transform.rotation;
};

target の transform を取得する

使用している具体的な target オブジェクトを通じて、シーン内 target の transform を取得できます。たとえば image tracking では、その target は ImageTargetController コンポーネントが付いているオブジェクトです。

Session.PostSessionUpdate += () =>
{
    var position = target.transform.position;
    var rotation = target.transform.rotation;
};

[任意] pose を取得する

pose は物体の位置と向きを表すデータ構造で、通常は position と rotation の 2 つの部分から構成されます。AR アプリでは、pose は通常、物理カメラやトラッキング対象の位置と向きをある参照系に対して表すために使われます。

Unity には生の pose データは用意されていません。なぜなら pose は通常シーン内オブジェクトの移動を駆動するために使われ、それこそが session が自動で行う処理だからです。コンテンツの計算と描画には transform だけで十分です。

重要

以下の方法を読む前に、シーン内のカメラ、トラッキング対象などの transform データがすでに要件を満たしていないか、もう一度考えてください。通常、追加の pose データは必要ありません。

どうしても何らかの理由で pose データが必要な場合は、PostSessionUpdate イベント内で transform から必要な pose 値を計算できます。通常は、PostSessionUpdate で取得できる target と camera の相対 transform がそのまま pose です。

次のコードは camera と target の transform を取得し、それらの相対 pose を計算する方法を示します。

Session.PostSessionUpdate += () =>
{
    Pose cameraToWorld = new(Session.Assembly.Camera.transform.position, Session.Assembly.Camera.transform.rotation);
    Pose targetToWorld = new(target.transform.position, target.transform.rotation);
    Pose worldToTarget = new()
    {
        position = Quaternion.Inverse(targetToWorld.rotation) * (-targetToWorld.position),
        rotation = Quaternion.Inverse(targetToWorld.rotation)
    };
    Pose cameraToTarget = cameraToWorld.GetTransformedBy(worldToTarget);
};
注意

AR Foundation、ヘッドセット、その他のサードパーティライブラリも同時に使っている場合、それらのライブラリもシーン内カメラの transform を変更することがあります。関連する pose 計算は、それらの更新ロジックが完了した後に行う必要があります。そうしないと結果が正しくない可能性があります。このような場面でも、PostSessionUpdate 内での target と origin の相対 pose は正確です。

[任意] transform 更新を横取りする

AR 機能が動作している間、Unity のカメラやトラッキング対象などのオブジェクト transform は通常 session によって自動更新されます。これらの更新は AR 描画の正しさと一貫性を保証するため、横取りする方法はありません。

ただし、オブジェクトの transform 更新ロジックを独自化したい場合は、PostSessionUpdate イベントを監視することで実現できます。ここでは少し面倒な方法を使う必要があります。

  1. 通常は描画コンテンツを session が制御するオブジェクトの子ノードや追加コンポーネントとして配置しますが、transform を独自更新したい場合は、それらのオブジェクトを session 制御の階層から外す必要があります。つまり、それらのオブジェクトは session 制御オブジェクトの子ノードであってはいけません。
  2. PostSessionUpdate イベント内で、独自更新したいオブジェクトの transform を記録します。
  3. 最後に、PostSessionUpdate イベント内で、session が提供するデータに基づき独自ロジックでこれらのオブジェクトの transform を更新します。
注記

PostSessionUpdate イベントを使うことが必須です。というのも、その時点を過ぎて初めて session はシーン内オブジェクトを操作しなくなるからです。

この方法は camera の変更には使えません。camera の独自更新には、より複雑なロジックが必要です。

また、この方法はオブジェクトの transform を独自更新するためだけに使え、session 制御オブジェクトの transform を変更するためには使えません。session 制御オブジェクトの transform が外部から変更されても、session は次の frame 更新時にその変更を上書きし続けるため、いくつかの計算結果に影響する可能性があります。

注意

この方法を使うには、オブジェクト transform の正しさを保証する必要があります。そうしないと AR 描画エラーにつながる可能性があります。

AR Foundation、ヘッドセット、その他のサードパーティライブラリも使用している場合、それらのライブラリがシーン内オブジェクトの transform を変更することがあります。それらの更新ロジックと独自ロジックが衝突しないようにしてください。そうしないと予期しない結果になる可能性があります。

関連トピック