在 AR 基金會 Android 應用程式中使用深度

深度 API 可協助裝置的相機瞭解場景中實際物體的大小和形狀。這項功能會使用相機建立深度圖片或深度地圖,進而在應用程式中加入 AR 寫實程度層。您可以運用深度圖片提供的資訊,精準地將虛擬物件顯示在真實世界中或實際物件後方,為使用者帶來身歷其境的沉浸式體驗。

深度資訊是根據動作計算而得,可能會與硬體深度感應器 (例如飛行時間 (ToF) 感應器) 的資訊 (如果有的話) 合併計算。裝置不需要 ToF 感應器來支援 Depth API

必要條件

請務必先瞭解基本 AR 概念,以及如何設定 ARCore 工作階段,然後再繼續操作。

將應用程式設為 Depth RequiredDepth 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,將應用程式設為選用深度。

啟用深度

為了節省資源,ARCore 預設不會啟用 Depth API。如要在支援的裝置上利用深度,您必須使用 CameraARCameraBackground 元件,將 AROcclusionManager 元件手動新增至 AR 相機的遊戲物件。詳情請參閱 Unity 說明文件中的自動遮蔽

請在新的 ARCore 工作階段中,檢查使用者的裝置是否支援深度和深度 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 的深度 API 提供的值等於投影至主體軸的 CA 長度。也稱為相對於相機來源 CA Z 座標。使用 Depth API 時,請務必瞭解深度值不是光線 CA 本身的長度,而是其「投影」

遮蔽虛擬物件,以視覺化方式呈現深度資料

請參閱 Unity 的網誌文章,瞭解深度資料的概要總覽,以及如何使用資料遮蔽虛擬圖片。此外,Unity 的 ARFoundation 範例會示範遮蔽虛擬圖像,並以視覺化方式呈現深度資料。

您可以使用兩道轉譯或個別物件的前向轉譯來呈現遮蔽。每種方法的效率取決於場景的複雜程度及其他應用程式具體考量。

個別物件、正向傳遞轉譯

個別物件的正向傳遞轉譯會決定其 Material 著色器中每個像素的遮蔽。如果無法看見像素,就會遭到裁切 (通常是透過 Alpha 混合) 裁剪,在使用者裝置上模擬遮蔽。

兩段式轉譯

採用兩段轉譯機制時,第一個傳遞會將所有虛擬內容算繪到中介緩衝區中。第二通道會根據實際景深與虛擬場景深度之間的差異,將虛擬場景融入背景。這個方法不需要額外的物件專用著色器運作,而且通常能產生比正向傳遞方法更一致的結果。

擷取深度圖片的距離

如要將深度 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;
}

後續步驟