AR Camera Manager コンポーネント
ARCameraManager コンポーネントでは、デバイスカメラのテクスチャの管理や光源推定モードを設定するプロパティの管理など、AR (拡張現実) カメラの機能を有効にします。デフォルトでは、AR Camera Manager コンポーネントは、XR Origin の子である Main Camera ゲームオブジェクトに存在します。
AR Camera Manager コンポーネント
設定 | 機能 |
---|---|
Auto Focus | ハードウェアカメラのオートフォーカスモードを有効または無効にします。無効の場合、フォーカスは固定され、自動的には変更されません。ノート: Auto Focus が使用できるかどうかはカメラハードウェアによるため、この設定値はランタイムに無視される場合があります。 |
Light Estimation | 環境のライティング特性を推定します。以下の 5 つのオプションがあります。
|
Facing Direction | パススルー映像に使用するカメラを制御します。World または User を指定できます。スマートフォンやタブレットなどのハンドヘルドモバイルデバイスでは、World は背面カメラ、User は前面カメラ (つまり "自撮り") です。 |
AR Camera Background
ARCameraBackground コンポーネントは、ランタイムにデバイスカメラからの映像フィードをシーンの背景としてレンダリングします。デフォルトでは、XR Origin の子である Main Camera ゲームオブジェクトに AR Camera Background コンポーネントが存在します。
Note
AR Camera Background コンポーネントがないと、デバイスカメラの映像を AR コンテンツの背景として表示することができません。
AR Camera Background コンポーネント
Use Custom Material を true に設定すると、ARCameraBackground コンポーネントは、指定したカスタムマテリアルを背景のレンダリングに使用します。カスタムマテリアルプロパティは任意であるため、通常、設定する必要はありません。各プロバイダープラグインパッケージには、それ自体の背景レンダリング用シェーダーが含まれています。
複数の XR Origin がある場合 (異なるスケールで異なるコンテンツを選択的にレンダリングする場合など)、XR Origin ごとに別のカメラを使用し、ARCameraBackground には別の 1 台のカメラを使用する必要があります。
ユニバーサルレンダーパイプライン (URP) での ARCameraBackground の設定
URP で ARCameraBackground を使用するには、AR Background Renderer Feature を有効にする必要があります。詳細については、ユニバーサルレンダーパイプライン を参照してください。
自動オクルージョン
デバイスによっては、現実世界の深度情報を提供するものもあります。例えば、A12 Bionic チップ (およびそれ以降) を搭載した iOS デバイスで人物オクルージョンと呼ばれる機能を使用すると、AR カメラフレームで検出された人物の深度情報が提供されます。LiDAR スキャナーを搭載した新しい Android スマートフォンや iOS デバイスでは、デバイスと物理的な環境との間の深度推定値が各ピクセルに含まれた環境深度画像を提供できます。
AROcclusionManager コンポーネントを ARCameraBackground コンポーネントと一緒にカメラに追加すると、深度バッファをレンダリングする際に、利用可能なすべての深度情報を背景レンダリングパスに自動的に組み込むことができます。これにより、レンダリングされたジオメトリを、現実世界から検出されたジオメトリによって遮蔽できるようになります。例えば、人物オクルージョンをサポートする iOS デバイスの場合、検出された人物によって、背後に存在するレンダリングコンテンツが遮蔽されます。
GPU のカメラ画像にアクセスする際にレンダーテクスチャへカメラテクスチャをコピー
カメラテクスチャは 外部テクスチャ である可能性が高いため、フレーム境界を越えて持続しない場合があります。カメラ画像を レンダーテクスチャ にコピーして持続させたり、さらに処理したりできると便利なことがあります。以下のコードでは、レンダーターゲットを消去 し、選択したレンダーテクスチャに直ちに GPU コピーまたは "Blit" (転送) を実行する コマンドバッファ を設定します。
var commandBuffer = new CommandBuffer();
commandBuffer.name = "AR Camera Background Blit Pass";
var texture = !m_ArCameraBackground.material.HasProperty("_MainTex") ? null : m_ArCameraBackground.material.GetTexture("_MainTex");
Graphics.SetRenderTarget(renderTexture.colorBuffer, renderTexture.depthBuffer);
commandBuffer.ClearRenderTarget(true, false, Color.clear);
commandBuffer.Blit(texture, BuiltinRenderTextureType.CurrentActive, m_ArCameraBackground.material);
Graphics.ExecuteCommandBuffer(commandBuffer);
ノート: Graphics.SetRenderTarget は、コマンドバッファの実行後に、現在のレンダーターゲットを上書きします。
CPU 上のデバイスカメラ画像へのアクセス
CPU 上のデバイスカメラ画像には、ARCameraManager.TryAcquireLatestCpuImage を使用してアクセスできます。
ARCameraManager.TryAcquireLatestCpuImage
を呼び出すと、テクスチャが GPU から CPU に転送されます。これは大量のリソースを消費してパフォーマンスに影響を及ぼす処理であるため、このメソッドの使用は、CPU で実行するコードで使用される、デバイスカメラからのピクセルデータにアクセスする必要がある場合に限定する必要があります。例えば、ほとんどのコンピュータービジョンコードでは、このデータに CPU でアクセスできるようにする必要があります。
テクスチャの数やフォーマットはプラットフォームによって異なります。
デバイスカメラ画像の CPU コピーを操作するには、まず ARCameraManager
を使用して XRCpuImage
を取得する必要があります。
public bool TryAcquireLatestCpuImage(out XRCpuImage cpuImage)
XRCpuImage
はネイティブリソースを表す struct
です。アプリケーションで不要となった場合は、Dispose
を呼び出してシステムに戻す必要があります。複数のフレームに対して XRCpuImage
を保持できますが、ほとんどのプラットフォームではフレーム数に制限があるため、Dispose
に失敗するとシステムから新しいデバイスカメラ画像が提供されなくなる可能性があります。
XRCpuImage
を使用すると、以下の 3 つの機能にアクセスできます。
Raw 画像プレーン
Note
この文脈で画像 "プレーン" とは、映像フォーマットで使用されるチャンネルのことです。平面ではないので、ARPlane
とは関係ありません。
ほとんどの映像フォーマットでは YUV エンコーディングバリアントが使用されています。Y は輝度プレーンであり、UV プレーンには色度情報が含まれています。U と V はインターリーブつまり別個のプレーンであり、ピクセルまたは行ごとに追加のパディングが生じる場合があります。
プラットフォーム固有の Raw YUV データにアクセスする必要がある場合は、XRCpuImage.GetPlane
メソッドを使用して各画像 "プレーン" を取得できます。
例
if (!cameraManager.TryAcquireLatestCpuImage(out XRCpuImage image))
return;
// 各画像プレーンを考慮します。
for (int planeIndex = 0; planeIndex < image.planeCount; ++planeIndex)
{
// 画像プレーンに関する情報をログに記録します。
var plane = image.GetPlane(planeIndex);
Debug.LogFormat("Plane {0}:\n\tsize: {1}\n\trowStride: {2}\n\tpixelStride: {3}",
planeIndex, plane.data.Length, plane.rowStride, plane.pixelStride);
// データを使用して何かを実行します。
MyComputerVisionAlgorithm(plane.data);
}
// リソースがリークしないように XRCpuImage を破棄します。
image.Dispose();
XRCpuImage.Plane
は NativeArray<byte>
を介してネイティブメモリバッファへの直接アクセスを提供します。これはネイティブメモリの "ビュー" を表します。NativeArray
を破棄する必要はなく、データは XRCpuImage
が破棄されない限り有効です。このメモリは読み取り専用と考える必要があります。
グレースケールとカラーへの同期変換
カメラ画像のグレースケールバージョンまたはカラーバージョンを取得するには、Raw プレーンデータを変換する必要があります。XRCpuImage
には同期と非同期の両方の変換メソッドが用意されています。ここでは、同期メソッドについて説明します。
このメソッドでは、XRCpuImage
を conversionParams
で指定された TextureFormat
に変換し、destinationBuffer
のバッファにデータを書き込みます。通常、グレースケール画像 (TextureFormat.Alpha8
と TextureFormat.R8
) は非常に高速ですが、カラー変換には CPU 負荷の大きい計算が必要です。
public void Convert(XRCpuImage.ConversionParams conversionParams, IntPtr destinationBuffer, int bufferLength)
以下は、XRCpuImage.ConversionParams
の詳細です。
public struct ConversionParams
{
public RectInt inputRect;
public Vector2Int outputDimensions;
public TextureFormat outputFormat;
public Transformation transformation;
}
プロパティ | 説明 |
---|---|
inputRect |
XRCpuImage の変換対象部分。これには、画像全体を指定することも画像の一部の矩形を指定することもできます。inputRect は元の画像内に完全に収まっている必要があります。画像内の必要な部分がわかっている場合は、元画像の矩形部分を変換する方がかなり速くなる可能性があります。 |
outputDimensions |
出力画像の寸法です。XRCpuImage コンバータは (最近傍を使用した) ダウンサンプリングをサポートしているため、inputRect.width および inputRect.height パラメーターよりも小さい出力画像を指定できます。例えば、(inputRect.width / 2, inputRect.height / 2) を指定すると、半分の解像度の画像を取得できます。これにより、色変換の実行にかかる時間を短縮することができます。outputDimensions は inputRect の寸法以下である必要があります (アップサンプリング不可)。 |
outputFormat |
現在、以下のフォーマットがサポートされています。
XRCpuImage.FormatSupported を使用してテクスチャフォーマットをテストすることもできます。 |
transformation |
このプロパティを使用して、変換中に適用する変換方法 (画像の X 軸反転、Y 軸反転、両軸反転など) を指定します。通常、これによって処理時間が増えることはありません。 |
変換先のバッファを指定する必要があるため、変換済みの画像を格納するために必要なバイト数も把握しておく必要があります。必要なバイト数を取得するには、以下を使用します。
public int GetConvertedDataSize(Vector2Int dimensions, TextureFormat format)
変換によって生成されるデータは、Texture2D.LoadRawTextureData
を使用した場合の Texture2D
と互換性があります。
例
using System;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
public class CameraImageExample : MonoBehaviour
{
Texture2D m_Texture;
void OnEnable()
{
cameraManager.cameraFrameReceived += OnCameraFrameReceived;
}
void OnDisable()
{
cameraManager.cameraFrameReceived -= OnCameraFrameReceived;
}
unsafe void OnCameraFrameReceived(ARCameraFrameEventArgs eventArgs)
{
if (!cameraManager.TryAcquireLatestCpuImage(out XRCpuImage image))
return;
var conversionParams = new XRCpuImage.ConversionParams
{
// 画像全体を取得します。
inputRect = new RectInt(0, 0, image.width, image.height),
// 2 でダウンサンプリングします。
outputDimensions = new Vector2Int(image.width / 2, image.height / 2),
// RGBA フォーマットを選択します。
outputFormat = TextureFormat.RGBA32,
// 縦軸に沿って反転させます (鏡像)。
transformation = XRCpuImage.Transformation.MirrorY
};
// 最終的な画像を格納するために必要なバイト数を確認します。
int size = image.GetConvertedDataSize(conversionParams);
// 画像を格納するためのバッファを確保します。
var buffer = new NativeArray<byte>(size, Allocator.Temp);
// 画像データを抽出します。
image.Convert(conversionParams, new IntPtr(buffer.GetUnsafePtr()), buffer.Length);
// 画像は RGBA32 フォーマットに変換され、指定したバッファに書き込まれるため、
// XRCpuImage を破棄できます。これをしないと、リソースがリークします。
image.Dispose();
// この時点で、画像を加工したり、コンピュータービジョンのアルゴリズムに渡したりすることなどができます。
// この例では、テクスチャに適用して可視化します。
// データを取得したので、テクスチャに落とし込んで可視化しましょう。
m_Texture = new Texture2D(
conversionParams.outputDimensions.x,
conversionParams.outputDimensions.y,
conversionParams.outputFormat,
false);
m_Texture.LoadRawTextureData(buffer);
m_Texture.Apply();
// 一時データは処理が完了したので、破棄できます。
buffer.Dispose();
}
}
グレースケールとカラーへの非同期変換
現在の画像が今すぐに必要でない場合は、XRCpuImage.ConvertAsync
を使用して非同期的に変換できます。非同期の画像リクエストは必要な回数だけ行うことができます。通常、次のフレームまでにはリクエストの準備が整いますが、未処理のリクエスト数には制限がないため、キューに複数のイメージがある場合はリクエストに時間がかかる場合があります。リクエストは受信順に処理されます。
XRCpuImage.ConvertAsync
は XRCpuImage.AsyncConversion
を返します。これにより、変換の状態を照会したり、変換が完了したらピクセルデータを取得したりできます。
変換オブジェクトを取得したら、そのステータスを照会して、変換が完了したかどうかを確認できます。
XRCpuImage.AsyncConversion conversion = image.ConvertAsync(...);
while (!conversion.status.IsDone())
yield return null;
リクエストが完了したかどうかを判断するには、status
を使用します。status が XRCpuImage.AsyncConversionStatus.Ready
の場合、GetData<T>
を呼び出してピクセルデータを NativeArray<T>
として取得できます。
GetData<T>
は NativeArray<T>
を返します。これはネイティブメモリの直接の "ビュー" であり、XRCpuImage.AsyncConversion
で Dispose
を呼び出すまで有効です。XRCpuImage.AsyncConversion
が破棄された後に NativeArray<T>
にアクセスするとエラーが発生します。GetData<T>
が返す NativeArray<T>
は破棄する必要はありません。
Important
XRCpuImage.AsyncConversion
は明示的に破棄する必要があります。XRCpuImage.AsyncConversion
を破棄しないと、XRCameraSubsystem
が除去されるまでメモリがリークします。XRCameraSubsystem
が除去されると、非同期変換もすべて削除されます。
Note
XRCpuImage
は非同期変換が完了する前に破棄できます。XRCpuImage.AsyncConversion
に含まれているデータは、XRCpuImage
に関連付けられていません。
例
Texture2D m_Texture;
public void GetImageAsync()
{
// デバイスカメラの画像に関する情報を取得します。
if (cameraManager.TryAcquireLatestCpuImage(out XRCpuImage image))
{
// 成功したら、画像の準備が整うまで待機するコルーチンを起動してから
// 画像をテクスチャに適用します。
StartCoroutine(ProcessImage(image));
// 非同期処理が完了するまでに画像を破棄しておくと安全です。
image.Dispose();
}
}
IEnumerator ProcessImage(XRCpuImage image)
{
// 非同期変換リクエストを作成します。
var request = image.ConvertAsync(new XRCpuImage.ConversionParams
{
// 画像全体を使用します。
inputRect = new RectInt(0, 0, image.width, image.height),
// 2 でダウンサンプリングします。
outputDimensions = new Vector2Int(image.width / 2, image.height / 2),
// カラー画像のフォーマット。
outputFormat = TextureFormat.RGB24,
// Y 軸に沿って反転させます。
transformation = XRCpuImage.Transformation.MirrorY
});
// 変換が完了するまで待機します。
while (!request.status.IsDone())
yield return null;
// 正常に変換が完了したかどうか、ステータスを確認します。
if (request.status != XRCpuImage.AsyncConversionStatus.Ready)
{
// 何らかの不具合が発生しました。
Debug.LogErrorFormat("Request failed with status {0}", request.status);
// エラーが発生した場合も破棄します。
request.Dispose();
yield break;
}
// 画像データの準備が整いました。これを Texture2D に適用しましょう。
var rawData = request.GetData<byte>();
// 必要に応じてテクスチャを作成します。
if (m_Texture == null)
{
m_Texture = new Texture2D(
request.conversionParams.outputDimensions.x,
request.conversionParams.outputDimensions.y,
request.conversionParams.outputFormat,
false);
}
// 画像データをテクスチャにコピーします。
m_Texture.LoadRawTextureData(rawData);
m_Texture.Apply();
// リクエストを破棄して、リクエストに関連付けられている
// リソース (Raw データなど) を削除する必要があります。
request.Dispose();
}
また、ConvertAsync
にはデリゲートを受け入れ、XRCpuImage.AsyncConversion
を返さないバージョンもあります。
public void GetImageAsync()
{
// デバイスカメラの画像に関する情報を取得します。
if (cameraManager.TryAcquireLatestCpuImage(out XRCpuImage image))
{
// 成功したら、画像の準備が整うまで待機するコルーチンを起動してから
// 画像をテクスチャに適用します。
image.ConvertAsync(new XRCpuImage.ConversionParams
{
// 画像全体を取得します。
inputRect = new RectInt(0, 0, image.width, image.height),
// 2 でダウンサンプリングします。
outputDimensions = new Vector2Int(image.width / 2, image.height / 2),
// カラー画像のフォーマット。
outputFormat = TextureFormat.RGB24,
// Y 軸に沿って反転させます。
transformation = CameraImageTransformation.MirrorY
// 非同期処理が完了したら ProcessImage を呼び出します。
}, ProcessImage);
// 非同期処理が完了するまでに画像を破棄しておくと安全です。
image.Dispose();
}
}
void ProcessImage(XRCpuImage.AsyncConversionStatus status, XRCpuImage.ConversionParams conversionParams, NativeArray<byte> data)
{
if (status != XRCpuImage.AsyncConversionStatus.Ready)
{
Debug.LogErrorFormat("Async request failed with status {0}", status);
return;
}
// Texture2D にコピーしたり、コンピュータービジョンアルゴリズムに渡したりするなど、何か有用なことをします。
DoSomethingWithImageData(data);
// データは戻るときに除去されるため、破棄する必要はありません。
}
このバージョンでも、NativeArray<byte>
はリクエストに関連付けられているネイティブメモリの "ビュー" であり、これを破棄する必要はありません。これはデリゲート呼び出しの継続時間だけ有効で、制御が戻るとすぐに除去されます。デリゲートの生存期間を超えてデータを持続させる必要がある場合は、コピーを作成します (NativeArray<T>.CopyTo
および NativeArray<T>.CopyFrom
を参照)。