Sessionの実行結果を取得
sessionの実行中は、シーンの一部オブジェクトのtransformが変更され、カメラの映像などが更新されます。場合によっては、これらの変更がアプリケーションの要件を満たさず、sessionの各フレームの実行結果を取得してデータを二次処理する必要があるかもしれません。このドキュメントでは、これらの結果データを取得して使用する方法について説明します。
開始する前に
- ARSession 概要 を読み、sessionの基本概念、構成、ワークフローを理解する
- sessionの作成 方法を理解する
- AR機能コンポーネントへのアクセス 方法を理解する
InputFrame の更新を取得
InputFrameUpdate イベントを使用して InputFrame の更新を取得できます。このイベントは、sessionがフレームごとに出力するデータの中で InputFrame に変更があった場合にのみトリガーされます。
注記
InputFrameUpdate は、EasyARがレンダリングを行うsessionでのみ有効です。一般的に、AR Foundationやヘッドマウントディスプレイを使用する場合は無効であり、その場合はこれらのサードパーティライブラリが提供する方法を使用してデータ更新を取得する必要があります。
InputFrame を使用すると、物理カメラ画像、カメラパラメータ、タイムスタンプ、ワールド座標系に対する物理カメラのtransform、トラッキング状態などを取得できます。ただし、カメラのtransformはsessionによって仮想カメラや他のオブジェクトに既に適用されているため、通常は InputFrame からカメラtransformを取得する必要はありません。
現在フレームの物理カメラ画像を取得
InputFrame.image() メソッドを使用して、Image 型の物理カメラ画像データを取得できます。
例えば、以下のコードは InputFrame 更新時に物理カメラ画像を取得します:
Session.InputFrameUpdate += (inputFrame) => {
using (var image = inputFrame.image())
{
}
};
注意
Image 型のデータ、およびそこから取得される他のclass型データを使用する場合、Dispose() が正しく呼び出されることを保証する必要があります(上記コードのusingステートメントがこれを保証します)。そうしないと、メモリリークや画像更新の停止などの問題が発生する可能性があります。
InputFrame や Image を次のフレームで使用するために保持する必要がある場合、ARAssembly.ExtraBufferCapacity の値を保持するデータ量に応じて増やす必要があります。そうしないと、バッファ不足によりデータの取得に失敗する可能性があります。
InputFrame を保持する必要がある場合、Clone() メソッドを呼び出して参照のコピーを作成し、不要になった時点でそのコピーに対して Dispose() を呼び出す必要があります。
物理カメラのフレームレートは通常レンダリングフレームレートよりも低いため、すべてのレンダリングフレームで InputFrameUpdate イベントが発生するわけではありません。同様に、物理カメラ画像のレンダリングもすべてのレンダリングフレームで更新されるわけではありません。InputFrameUpdate が次にトリガーされるまでのすべてのレンダリングフレームの画像内容は、現在の InputFrame の画像と一致します。
注記
InputFrame 内の画像は、必ず現在のフレームの仮想カメラ背景画像と一致します。ただし、背景画像のレンダリング時にスケーリングやクロップが行われる可能性があるため、取得した画像のサイズやアスペクト比が画面上の表示と一致しないことは正常です。
さらに重要なのは、InputFrame.image() が返す画像データはCPUが読み取り可能であり、GPUテクスチャではないことです。GPU上で画像データを使用する必要がある場合は、画像データをGPUテクスチャにアップロードするか、CameraImageRenderer.RequestTargetTexture(Action<Camera, RenderTexture>) インターフェースを介して直接GPUテクスチャを取得する必要があります。
[オプション] 物理カメラ画像レンダリングをインターセプト
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計算はすべてのレンダリングフレームで実行されています。したがって、すべてのtransformの変更を取得する必要がある場合は、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を取得できます。例えば、画像トラッキングの場合、この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 イベントをリッスンすることで実現できます。これには少し煩雑な方法を使用する必要があります:
- 通常は、レンダリングコンテンツをsessionが制御するオブジェクトの子ノードまたは追加コンポーネントとしてアタッチすべきですが、オブジェクトのtransformをカスタム更新する必要がある場合は、これらのオブジェクトをsessionが制御するオブジェクト階層から削除する必要があります。つまり、これらのオブジェクトはsession制御オブジェクトの子ノードであるべきではありません。
- PostSessionUpdate イベント内で、カスタム更新したいオブジェクトのtransformを記録します。
- 最後に、PostSessionUpdate イベント内で、sessionが提供するデータに基づいて、カスタムロジックを使用してこれらのオブジェクトのtransformを更新します。
注記
PostSessionUpdate イベントの使用が必須です。なぜなら、この時間以降でなければ、sessionはシーン内のオブジェクトを操作しないからです。
この方法はカメラの変更には使用できず、カメラのカスタム更新を処理するためにはより複雑なロジックが必要となることに注意してください。
また、この方法はオブジェクトのtransformをカスタム更新するためにのみ使用でき、sessionが制御するオブジェクトのtransformを変更するために使用することはできません。sessionが制御するオブジェクトのtransformが外部から変更された場合、sessionは依然として次のフレーム更新時にこれらの変更を上書きし、いくつかの計算の正確性に影響を与える可能性があります。
注意
この方法を使用するには、オブジェクトtransformの正確性を保証する必要があります。そうしないと、ARレンダリングエラーの原因となる可能性があります。
もし同時にAR Foundation、ヘッドマウントディスプレイ、または他のサードパーティライブラリも使用している場合、これらのライブラリもシーン内のオブジェクトのtransformを変更する可能性があります。これらのライブラリの更新ロジックとカスタムロジックが競合しないことを保証する必要があります。そうしないと、予期しない結果を招く可能性があります。