AR Foundation Android アプリで Depth を使用する

Depth API により、デバイスのカメラはシーン内の実際のオブジェクトのサイズと形状を認識できます。カメラを使用して奥行き画像(奥行きマップ)を作成し、アプリに AR のリアルなレイヤを追加します。奥行きのある画像から得られる情報を使用して、仮想オブジェクトを現実世界のオブジェクトの前または背後に正確に表示し、没入感のあるリアルなユーザー エクスペリエンスを実現できます。

奥行き情報は動きから計算され、利用可能であれば、Time of Flight(ToF)センサーなどのハードウェア深度センサーからの情報と組み合わせることができます。Depth API をサポートするため ToF センサーは必要ありません

前提条件

続行する前に、AR の基本的なコンセプトARCore セッションを構成する方法を理解しておいてください。

アプリを Depth Required または Depth Optional に設定する(Android のみ)

アプリで Depth API のサポートが必要な場合(AR エクスペリエンスのコア部分が奥行きに依存している場合、またはアプリの奥行きを使用する部分にグレースフル フォールバックがないため)、Google Play ストアでのアプリの配信を Depth API をサポートするデバイスに制限できます。

アプリを Depth Required にする

Edit > Project Settings > XR Plug-in Management > ARCore に移動します。

Depth はデフォルトで Required に設定されています。

アプリを Depth Optional にする

  1. Edit > Project Settings > XR Plug-in Management > ARCore に移動します。

  2. Depth プルダウン メニューから Optional を選択して、アプリの Depth をオプションに設定します。

Depth を有効にする

リソースを節約するため、ARCore のデフォルトでは Depth API が有効になっていません。サポートされているデバイスで奥行きの機能を利用するには、Camera コンポーネントと ARCameraBackground コンポーネントを使用して、AROcclusionManager コンポーネントを AR カメラ ゲーム オブジェクトに手動で追加する必要があります。詳しくは、Unity ドキュメントの自動オクルージョンをご覧ください。

新しい ARCore セッションでは、次のように、ユーザーのデバイスが深度と Depth API をサポートしているかどうかを確認します。

// Reference to AROcclusionManager that should be added to the AR Camera
// game object that contains the Camera and ARCameraBackground components.
var occlusionManager = …

// Check whether the user's device supports the Depth API.
if (occlusionManager.descriptor?.supportsEnvironmentDepthImage)
{
    // If depth mode is available on the user's device, perform
    // the steps you want here.
}

深度画像を取得する

AROcclusionManager から最新の環境の深度画像を取得します。

// Reference to AROcclusionManager that should be added to the AR Camera
// game object that contains the Camera and ARCameraBackground components.
var occlusionManager = …

if (occlusionManager.TryAcquireEnvironmentDepthCpuImage(out XRCpuImage image))
{
    using (image)
    {
        // Use the texture.
    }
}

未加工の CPU イメージを RawImage に変換すると、柔軟性が向上します。これを行う方法の例については、Unity の ARFoundation サンプルをご覧ください。

奥行きの値を理解する

観測された実世界のジオメトリ上の点 A と、奥行きのある画像内の同じ点を表す 2D 点 a から、a の Depth API によって指定される値は、主軸に投影された CA の長さと等しくなります。これは、カメラの原点 C を基準とする A の Z 座標とも呼ばれます。Depth API を使用する場合は、深度の値は光線 CA 自体の長さではなく、光線の投影であることを理解することが重要です。

仮想オブジェクトのオクルージョンと奥行きデータの可視化

深度データの概要と、それを使用して仮想画像を隠す方法については、Unity のブログ投稿をご覧ください。また、Unity の ARFoundation サンプルでは、仮想画像のオクルージョンと奥行きデータの可視化を確認できます。

オクルージョンは、2 パス レンダリングまたはオブジェクトごとのフォワードパス レンダリングを使用してレンダリングできます。それぞれの方法の効率は、シーンの複雑さと、その他のアプリ固有の考慮事項によって決まります。

オブジェクトごとのフォワードパス レンダリング

オブジェクトごとのフォワードパス レンダリングでは、マテリアル シェーダー内のオブジェクトの各ピクセルのオクルージョンを決定します。見えないピクセルは、通常はアルファ ブレンディングによってクリップされ、ユーザーのデバイスでオクルージョンがシミュレートされます。

2 パス レンダリング

2 パス レンダリングでは、最初のパスがすべての仮想コンテンツを中間バッファにレンダリングします。2 つ目のパスでは、現実世界の奥行きと仮想シーンの奥行きの差に基づいて、仮想シーンが背景にブレンドされます。この方法では、オブジェクト固有のシェーダー作業を追加で必要なく、一般的に、フォワードパス方式よりも均一な結果が得られます。

奥行きのある画像から距離を抽出する

仮想オブジェクトのオクルージョンや深度データの可視化以外の目的で Depth API を使用するには、深度画像から情報を抽出します。

Texture2D _depthTexture;
short[] _depthArray;

void UpdateEnvironmentDepthImage()
{
  if (_occlusionManager &&
        _occlusionManager.TryAcquireEnvironmentDepthCpuImage(out XRCpuImage image))
    {
        using (image)
        {
            UpdateRawImage(ref _depthTexture, image, TextureFormat.R16);
            _depthWidth = image.width;
            _depthHeight = image.height;
        }
    }
  var byteBuffer = _depthTexture.GetRawTextureData();
  Buffer.BlockCopy(byteBuffer, 0, _depthArray, 0, byteBuffer.Length);
}

// Obtain the depth value in meters at a normalized screen point.
public static float GetDepthFromUV(Vector2 uv, short[] depthArray)
{
    int depthX = (int)(uv.x * (DepthWidth - 1));
    int depthY = (int)(uv.y * (DepthHeight - 1));

    return GetDepthFromXY(depthX, depthY, depthArray);
}

// Obtain the depth value in meters at the specified x, y location.
public static float GetDepthFromXY(int x, int y, short[] depthArray)
{
    if (!Initialized)
    {
        return InvalidDepthValue;
    }

    if (x >= DepthWidth || x < 0 || y >= DepthHeight || y < 0)
    {
        return InvalidDepthValue;
    }

    var depthIndex = (y * DepthWidth) + x;
    var depthInShort = depthArray[depthIndex];
    var depthInMeters = depthInShort * MillimeterToMeter;
    return depthInMeters;
}

次のステップ

  • Raw Depth API を使用すると、より正確なセンシングが可能になります。
  • ARCore Depth Lab をご確認ください。深度データにアクセスするさまざまな方法を解説しています。