使用空間容器建構

適用的 XR 裝置
本指南可協助您為這類 XR 裝置打造體驗。
XR 頭戴式裝置
有線 XR 眼鏡

空間容器可為 OpenXR 帶來立體視窗。空間容器由 OpenXR 1.1 中的 XR_EXT_spatial_container 和 XR_EXT_spatial_container_self_rendering 擴充功能定義,是使用者空間中由系統管理的 3D 體積區域,應用程式會使用標準 OpenXR 組合層在其中算繪 3D 內容。

傳統上,OpenXR 工作階段會要求獨占使用者整個視野的存取權。空間容器可讓應用程式在使用者環境中的有界 3D 方塊內,算繪 3D OpenXR 內容,或隨時轉換為無界的全螢幕沉浸式體驗。

關於空間容器

空間容器是由 Android XR 系統管理,並由 OpenXR 應用程式算繪的有界或無界 3D 區域:

  • 有界模式:應用程式會建議 3D 尺寸 (寬度、高度和深度,以公尺為單位),系統則會在使用者環境中分配 3D 定界框。應用程式會在立體空間中算繪立體 3D 內容,系統會將算繪內容裁剪至 3D 方塊的邊界。
  • 沉浸模式:空間容器會無限擴展,讓應用程式占滿使用者的整個視野,就像傳統的全螢幕 OpenXR 工作階段一樣。
在全螢幕模式中同時執行的三個空間容器:橘色地板鎖定 3D 地形容器、藍色 3D 鴨子模型 (附有 2D UI),以及綠色 2D UI 容器。
圖 1. 全螢幕模式中的多個空間容器:鎖定在地板上的 3D 地形體積 (左)、搭配 2D UI 的 3D 鴨子體積 (中),以及 2D UI 容器 (右)。

使用者如何與空間容器互動

Android XR 系統會管理空間容器的視窗控制項和實體空間中的位置,在各個應用程式中提供一致的互動模型:

  • 移動和重新定位:使用者可以使用手勢 (例如捏合) 或動作控制器,抓取空間容器的系統控點,在 3D 空間中重新定位。
  • 最小化和關閉:使用者可以透過系統視窗控制項,最小化或關閉個別空間容器。使用者關閉容器時,系統會通知應用程式,以便清理資源。
  • 透過場景理解功能鎖定在地板上:當應用程式宣告並要求 android.permission.SCENE_UNDERSTANDING_COARSE 權限時,系統會自動啟用表面貼齊功能,讓使用者將空間容器放置並鎖定在實體地板上,不必額外設定應用程式,如圖 1 的橘色地形容器所示。
  • 直接 3D 互動:當容器處於焦點且可互動時,使用者可以透過手勢追蹤、視線追蹤或 6DoF 控制器,直接與容器內的 3D 內容互動。
使用者透過手勢追蹤功能,與藍色空間容器中的 3D 鴨子互動。
圖 2. 使用者透過手勢追蹤,與受限空間容器中的 3D 內容互動。
  • 在有界和沉浸模式之間切換:使用者或應用程式可以在有界 3D 方塊和無界全螢幕沉浸檢視畫面之間切換容器,不必重新啟動 OpenXR 工作階段。
從有邊界的空間容器轉換為無邊界的全螢幕沉浸式檢視畫面後,3D 鴨子場景。
圖 3. 從受限模式轉換為沉浸模式後的空間容器,填滿使用者的視野。
  • 隨時隨地使用追蹤模式:系統會控制每個空間容器座標空間 (xrCreateSpatialContainerSpaceEXT) 的姿勢,因此使用者可以透過系統視窗控制項啟用追蹤模式,讓有界限的 3D 容器在他們走動時自動跟隨,並保持在視野範圍內,不需在應用程式中進行任何額外設定。

如何在 Android XR 中使用空間容器

使用空間容器建構應用程式時,請根據容器的邊界、狀態變更和背景混合模式設計場景。

調整 3D 內容,使其符合容器邊界

在有界模式下,空間容器空間 (xrCreateSpatialContainerSpaceEXT) 的原點 (0, 0, 0) 位於 3D 定界框的中心。由於系統會管理容器空間在使用者環境中的姿勢,因此應用程式會根據 (0, 0, 0) 算繪 3D 內容,而系統會處理使用者重新定位和跟隨模式。系統會裁剪在方塊寬度、高度或深度以外算繪的任何幾何圖形,確保內容不超出指定範圍。

應用程式應監聽執行階段的邊界變更事件,並縮放或重新定位 3D 場景,確保重要內容符合容器目前的尺寸。當容器轉換為沉浸模式時,邊界會變成無限,您可以將場景還原為完整的 1:1 世界比例。

回應容器狀態變更

每個空間容器都會回報自身狀態,讓應用程式能據此做出反應,而不只是在應用程式層級管理可見度和焦點:

  • Visibility:指出容器目前是顯示還是隱藏。在容器隱藏時,暫停動畫、音訊或模擬更新。應用程式也可以要求顯示或隱藏容器。
  • 可互動性:指出容器是否處於焦點狀態,並接收來自手勢追蹤、控制器或視線的使用者輸入內容。啟用或停用互動式醒目顯示,以及焦點變更時的輸入處理。
  • Bounds mode:指出容器目前處於 XR_SPATIAL_CONTAINER_BOUNDS_MODE_BOUNDED_EXT (裁剪為 3D 方塊) 或 XR_SPATIAL_CONTAINER_BOUNDS_MODE_IMMERSIVE_EXT (無邊界全螢幕)。模式變更時,更新場景比例並切換環境視覺效果 (例如天空盒或透視)。應用程式也可以隨時要求在邊界模式和沉浸模式之間切換。

在有界模式中使用 Alpha 混合透明度

在有界模式下,透明背景可讓使用者在 3D 模型周圍看到實際環境和其他視窗。請將攝影機或可視區域設為透明背景,並使用 Alpha 混合環境混合模式。切換至沉浸模式時,可以選擇保持穿透模式,或切換至不透明的 Skybox 環境。

確認功能可用性並設定資訊清單

Spatial Container 需要支援 Spatial API 級別 4 以上版本的 Android XR 裝置。請勿直接檢查 Android API 級別,而是查詢 OpenXR 執行階段,判斷是否支援空間容器。方法是呼叫 xrEnumerateInstanceExtensionProperties,確認 XR_EXT_spatial_container 和 XR_EXT_spatial_container_self_rendering 是否可用,並使用 XrSystemSpatialContainerPropertiesEXT 呼叫 xrGetSystemProperties,檢查 supportsBounded 和 supportsImmersive。

建構使用空間容器的應用程式前,請先在 AndroidManifest.xml 中設定活動啟動模式和任何功能專屬權限。

在 AndroidManifest.xml 中啟用全螢幕模式

空間容器需要全螢幕代管模式,即使是獨立 OpenXR 應用程式也一樣。傳統的全螢幕 OpenXR 應用程式會宣告 XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED,在沒有系統視窗管理的情況下,獨占螢幕控制權。相較之下,使用空間容器的應用程式會依賴 Android XR 系統合成器,在全螢幕模式下管理 3D 有界體積、視窗控制項和空間位置,且必須將 android.window.PROPERTY_XR_ACTIVITY_START_MODE 設為 XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application ...>
        <!-- Spatial Containers require Full Space Managed mode -->
        <property
            android:name="android.window.PROPERTY_XR_ACTIVITY_START_MODE"
            android:value="XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED" />

        <activity android:name=".MainActivity" ...>
            <!-- Optional hint declaring which bounds modes the activity uses -->
            <property
                android:name="org.khronos.openxr.PROPERTY_ACTIVITY_HINT_XR_SPATIAL_CONTAINER_USED_BOUNDS_MODES"
                android:value="XR_SPATIAL_CONTAINER_BOUNDS_MODE_BOUNDED_EXT|XR_SPATIAL_CONTAINER_BOUNDS_MODE_IMMERSIVE_EXT" />
        </activity>
    </application>
</manifest>

宣告及要求功能專屬權限

建立及算繪基本空間容器時,不需要危險的執行階段權限。不過,特定功能需要宣告並要求相應的 Android XR 權限:

  • 如要使用場景理解功能,將空間容器鎖定並錨定在實體地板上,請在執行階段宣告並要求危險的 android.permission.SCENE_UNDERSTANDING_COARSE 權限。
  • 如要在 OpenXR 中追蹤使用者的頭部姿勢,請宣告並要求 android.permission.HEAD_TRACKING。
<uses-permission android:name="android.permission.HEAD_TRACKING" />
<uses-permission android:name="android.permission.SCENE_UNDERSTANDING_COARSE" />

如要瞭解如何在執行階段宣告及要求危險的 XR 權限,請參閱「瞭解 XR 權限」。

在專案中啟用空間容器

您可以使用 Unity、Godot 或直接在 C++ 中使用 OpenXR,建構 Spatial Container。請選取分頁標籤,查看所需環境的必要條件、編輯器設定和程式碼範例:

Unity

必要條件

設定專案

  1. 依序前往「Edit」>「專案設定」>「XR Plug-in Management」>「OpenXR」。
  2. 按一下「Android XR (Extensions): Session Management」旁邊的「Settings」圖示 ,然後清除「Immersive XR」。
  3. 在「OpenXR Feature Groups」清單中,啟用「Spatial Container」。

在 C# 中使用 XRSpatialContainerFeature 查詢邊界、回應容器事件,以及在邊界和沉浸模式之間切換:

void OnEnable()
{
    XRSpatialContainerFeature.OnSpatialContainerBoundsChanged += OnBoundsChanged;
}

private void OnBoundsChanged(Vector3 bounds, bool infiniteBounds)
{
    float minScale = Mathf.Min(bounds.x, bounds.y, bounds.z);
    transform.localScale = infiniteBounds ? Vector3.one : Vector3.one * minScale;
}

public void SetImmersiveMode(bool immersive)
{
    XRSpatialContainerFeature.TryRequestBoundsMode(immersive);
}

如需完整的專案設定步驟、資訊清單設定和權限處理方式,請參閱「在 Unity 中使用空間容器」。

Godot

必要條件

設定專案

  1. 依序前往「Project」>「專案設定」,啟用「Advanced Settings」,然後依序前往「General」>「XR」>「OpenXR」>「Extensions」。
  2. 在「Spatial Container」(空間容器) 子節下方,選取「Enabled」(已啟用),然後設定「Bounds Mode」(邊界模式) 和「Bounds」(邊界)。
  3. 依序前往「Project」>「Export...」。>「Android」,然後選取「Android XR Features」下方的「Use Experimental Features」。

在 GDScript 中使用 OpenXRSpatialContainerExtension 單例模式,回應容器邊界和模式變更:

func _ready() -> void:
    if OpenXRSpatialContainerExtension.is_enabled():
        OpenXRSpatialContainerExtension.spatial_container_bounds_changed.connect(
            _on_bounds_changed
        )

func _on_bounds_changed(_rid: RID, bounds: Vector3) -> void:
    scale = Vector3.ONE * min(bounds.x, bounds.y, bounds.z)

func set_immersive_mode() -> void:
    OpenXRSpatialContainerExtension.request_main_spatial_container_bounds_mode(
        OpenXRSpatialContainerState.IMMERSIVE
    )

如需完整的專案設定、匯出權限和 GDScript 範例,請參閱「在 Godot 中使用空間容器」。

OpenXR

必要條件

建立 XrInstance 時,請啟用 XR_EXT_spatial_container 和 XR_EXT_spatial_container_self_rendering,然後建立及算繪空間容器:

// 1. Enable the Spatial Container extensions during xrCreateInstance
const char* enabledExtensions[] = {
    XR_EXT_SPATIAL_CONTAINER_EXTENSION_NAME,
    XR_EXT_SPATIAL_CONTAINER_SELF_RENDERING_EXTENSION_NAME,
};

// 2. Configure and create a self-rendering spatial container
XrSpatialContainerConfigurationSelfRenderingEXT selfRenderingConfig{
    .type = XR_TYPE_SPATIAL_CONTAINER_CONFIGURATION_SELF_RENDERING_EXT,
    .next = nullptr,
    .viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO,
    .suggestedBounds = {1.5f, 1.5f, 1.5f}, // Width, height, depth in meters
};

XrSpatialContainerCreateInfoEXT createInfo{
    .type = XR_TYPE_SPATIAL_CONTAINER_CREATE_INFO_EXT,
    .next = &selfRenderingConfig,
    .spatialContainerConfig =
        reinterpret_cast<const XrSpatialContainerConfigurationBaseHeaderEXT*>(
            &selfRenderingConfig),
};

XrSpatialContainerEXT spatialContainer = XR_NULL_HANDLE;
xrCreateSpatialContainerEXT(session, &createInfo, &spatialContainer);

// 3. Request bounded mode and make the container visible
xrRequestSpatialContainerBoundsModeEXT(
    spatialContainer, XR_SPATIAL_CONTAINER_BOUNDS_MODE_BOUNDED_EXT);
xrRequestSpatialContainerVisibilityEXT(spatialContainer, XR_TRUE);

另請參閱


OpenXR™ 和 OpenXR 標誌是 Khronos Group Inc. 擁有的商標,並已在中國、歐盟、日本和英國註冊為商標。