空間容器可為 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 工作階段一樣。
使用者如何與空間容器互動
Android XR 系統會管理空間容器的視窗控制項和實體空間中的位置,在各個應用程式中提供一致的互動模型:
- 移動和重新定位:使用者可以使用手勢 (例如捏合) 或動作控制器,抓取空間容器的系統控點,在 3D 空間中重新定位。
- 最小化和關閉:使用者可以透過系統視窗控制項,最小化或關閉個別空間容器。使用者關閉容器時,系統會通知應用程式,以便清理資源。
- 透過場景理解功能鎖定在地板上:當應用程式宣告並要求
android.permission.SCENE_UNDERSTANDING_COARSE權限時,系統會自動啟用表面貼齊功能,讓使用者將空間容器放置並鎖定在實體地板上,不必額外設定應用程式,如圖 1 的橘色地形容器所示。 - 直接 3D 互動:當容器處於焦點且可互動時,使用者可以透過手勢追蹤、視線追蹤或 6DoF 控制器,直接與容器內的 3D 內容互動。
- 在有界和沉浸模式之間切換:使用者或應用程式可以在有界 3D 方塊和無界全螢幕沉浸檢視畫面之間切換容器,不必重新啟動 OpenXR 工作階段。
- 隨時隨地使用追蹤模式:系統會控制每個空間容器座標空間 (
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
必要條件
- Unity 版本:Unity 6 6000.0.58f2 以上版本。
- 專案設定:完成 Unity 專案設定指南中的所有步驟。
- Unity 套件:安裝 1.5.0 以上版本的 Android XR Extensions for Unity 套件。
設定專案
- 依序前往「Edit」>「專案設定」>「XR Plug-in Management」>「OpenXR」。
- 按一下「Android XR (Extensions): Session Management」旁邊的「Settings」圖示 ,然後清除「Immersive XR」。
- 在「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
必要條件
- Godot 版本:Godot 4.8-dev 以上版本。
- 專案設定:完成 Godot Engine 專案設定指南中的所有步驟。
- Godot OpenXR Vendors Plugin:安裝 5.2 以上版本的 Godot OpenXR Vendors Plugin。
設定專案
- 依序前往「Project」>「專案設定」,啟用「Advanced Settings」,然後依序前往「General」>「XR」>「OpenXR」>「Extensions」。
- 在「Spatial Container」(空間容器) 子節下方,選取「Enabled」(已啟用),然後設定「Bounds Mode」(邊界模式) 和「Bounds」(邊界)。
- 依序前往「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
必要條件
- OpenXR 版本:OpenXR 1.1 標頭,包含
XR_EXT_spatial_container和XR_EXT_spatial_container_self_rendering。 - 資訊清單設定:按照「開始使用 Android XR 適用的 OpenXR」一文所述,使用
XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED設定應用程式資訊清單。
建立 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. 擁有的商標,並已在中國、歐盟、日本和英國註冊為商標。