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

Depth 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。參加 引進支援裝置時,必須手動將 AROcclusionManager敬上 元件新增至 AR 相機遊戲物件中的 CameraARCameraBackground 元件。詳情請見 自動遮蔽 請參閱 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 範例

瞭解深度值

觀察到的實際幾何圖形和 2D 點 a 上的已知點 A 代表深度圖片中的相同點,也就是深度圖片提供的值 a 處的 API 等於投影到主要軸的 CA 長度。 這也稱為相機相對於 A 的 z 座標 來源:C。使用 Depth API 時,請務必瞭解 深度值不是灰色 CA 本身的長度,而是 投影 我們很快就會深入探討 所以目前先概略介紹

忽略虛擬物件,並以圖表呈現深度資料

查看 Unity 的網誌文章 ,以便概略瞭解深度資料,以及如何使用這些資料遮蔽資料 虛擬映像檔此外,Unity 的 ARFoundation 範例 會呈現遮蓋的虛擬影像,以及以視覺化方式呈現深度資料。

您可以使用兩道算繪或個別物件的正向傳遞算繪,算繪遮蔽情形。每個方法的效率都取決於場景的複雜程度,以及其他應用程式特有的考量因素。

每個物件的正向傳遞轉譯

個別物件的正向循環算繪會決定該物件在其 Material 著色器中每個像素的遮蔽情形。如果像素看不見,就會遭到裁剪 (通常是透過 Alpha 混合),進而模擬使用者裝置上的遮蔽情形。

雙通道算繪

透過雙傳遞算繪,第一個傳遞會將所有虛擬內容算繪到中介緩衝區。第二張票證根據實際深度與虛擬場景深度之間的差異,將虛擬場景融入背景。這種方法不需要其他物件專屬的著色器工作,而且通常會產生比轉寄傳遞方法更加統一的結果。

擷取深度圖片的距離

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

後續步驟