設定 XR

Godot XR 系統簡介

Godot 提供模組化的 XR 系統,將各種 XR 平台的細節抽象化,讓使用者不需直接面對。核心元件為 XRServer,它作為 XR 系統的中央介面,讓使用者可以探索介面並與 XR 系統的各元件互動。

每個支援的 XR 平台都實作為一個 XRInterface。支援的平台清單可在功能一覽頁面 此處 找到。受支援的介面會自行向 XRServer 註冊,並可透過 XRServerfind_interface 方法查詢。找到所需的介面後,可呼叫該介面的 initialize 來進行初始化。

警告

已註冊的介面僅代表該介面可用;若主機系統不支援該介面,初始化可能會失敗並回傳 false。這可能有多種原因,而且不同平台原因各異。可能是使用者尚未安裝所需軟體,或者僅僅是沒有將頭戴式裝置插上。作為開發者,你必須正確處理介面初始化失敗的情況。

由於 XR 輸出有特殊需求,尤其是頭戴裝置需為每隻眼睛提供不同影像,Godot 的 XRServer 會覆蓋算繪系統的多項功能。對於獨立裝置,最終輸出會由 XRInterface 處理,Godot 預設的輸出系統會停用。對於作為第二螢幕的桌面 XR 裝置,可以指定獨立的 Viewport 負責 XR 輸出,讓主 Godot 視窗可用於顯示其他內容。

備註

請注意,僅有一個介面可以負責 XR 裝置的輸出,這稱為主要介面,預設會是第一個被初始化的介面。因此 Godot 目前僅支援單一頭戴裝置的實作。雖然仍可設置次要介面(例如為僅支援 3DOF 的裝置新增追蹤),但這種情況越來越罕見。

幾乎所有 XR 應用程式中都會用到三種 XR 專用節點型別:

  • XROrigin3D 代表你的遊戲空間中心點(雖然這樣說有點過度簡化,但後面會詳細說明)。所有由 XR 平台在實體空間追蹤的物件,都是以這個點為基準定位。

  • XRCamera3D 代表用於 XR 裝置算繪輸出的(立體)相機。這個節點的位置會由 XR 系統自動根據平台提供的追蹤資訊進行更新。

  • XRController3D 代表玩家的控制器,通常有兩個(左、右手各一)。這些節點可以存取控制器的各種狀態,並在玩家按下按鈕時發送訊號。節點位置同樣會由 XR 系統根據平台追蹤資訊自動更新。

還有其他 XR 相關節點,這三個節點也有更多細節,之後會再詳細介紹。

建議採用哪種算繪器

Godot 為專案提供三種算繪器選項:相容性、行動裝置與 Forward+。目前建議桌面 VR 專案使用行動裝置算繪器,而如 Meta Quest 3 這種獨立頭戴裝置則建議使用相容性算繪器。XR 專案雖可使用 Forward+ 算繪器,但相較於前兩者,目前對 XR 的最佳化尚不足。

OpenXR

OpenXR 是一項新興產業標準,能讓不同 XR 平台透過標準化 API 提供功能給 XR 應用。此標準由 Khronos Group 維護,屬於開放標準,與 Godot 的目標高度契合。

OpenXR 的 Vulkan 實作與 Vulkan 引擎緊密整合,甚至接管部分 Vulkan 系統。這要求在設定 XR 系統前,必須將某些核心圖形功能深度整合至 Vulkan 算繪器中。這也是將 OpenXR 納入核心介面的一大主因。

這也表示,Godot 啟動時必須啟用 OpenXR 才能正確設定。請至專案設定的 XR > OpenXR,確認 Enabled 已勾選。

../../_images/openxr_enabled.webp

此處還有其他與 OpenXR 相關的設定。這些設定在應用程式執行時無法更改。預設值已可正常啟用,如需詳細說明,請參閱 OpenXR 設定

另外,請到專案設定中的 XR > Shaders,勾選 Enabled,以啟用著色器。完成後,請點擊 儲存並重啟 按鈕。

../../_images/xr_shaders.webp

警告

許多後製(後處理)特效尚未更新以支援立體算繪。使用這些效果會造成顯示異常。

建立 XR 場景

每個 XR 應用程式至少需要 XROrigin3DXRCamera3D 節點。大多數情況下還會有兩個 XRController3D,分別代表左手與右手。請注意,相機與控制器節點應為原點節點的子節點。請將這些節點加入新場景,並將控制器節點分別命名為 LeftHandRightHand,場景結構大致如下:

../../_images/xr_basic_scene.webp

警告圖示是預期的,設定控制器後即可消失。請選取左手節點並設定如下:

../../_images/xr_left_hand.webp

右手設定如下:

../../_images/xr_right_hand.webp

目前這些節點都在地板上,執行時會自動定位。為方便開發,可將相機向上移動,令其 y 設為 1.7 ,並將控制器節點分別移至 -0.5, 1.0, -0.5 (左手)與 0.5, 1.0, -0.5 (右手)。

接著,請在根節點(root node)新增腳本,並加入以下程式碼:

extends Node3D

var xr_interface: XRInterface

func _ready():
    xr_interface = XRServer.find_interface("OpenXR")
    if xr_interface and xr_interface.is_initialized():
        print("OpenXR initialized successfully")

        # Turn off v-sync!
        DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)

        # Change our main viewport to output to the HMD
        get_viewport().use_xr = true
    else:
        print("OpenXR not initialized, please check if your headset is connected")

這段程式碼假設你使用的是 OpenXR,如果要改用其他介面,請修改 find_interface 的參數即可。

警告

如上述程式碼所示,我們關閉了垂直同步(v-sync)。使用 OpenXR 時,渲染結果會輸出到 HMD,通常需運作在 90Hz 或更高更新率。若你的螢幕是 60Hz 並啟用垂直同步,則輸出會被限制為每秒 60 影格。

像 OpenXR 這類 XR 介面會自行處理同步。

另請注意,物理引擎預設也是以 60Hz 執行,這可能導致物理表現不流暢。建議將 Engine.physics_ticks_per_second 設為更高的值。

若此時直接運作專案,雖然一切正常,但你會處於一個黑暗世界。因此,請在場景中加入 DirectionalLight3DWorldEnvironment 節點。你也可以暫時在每個控制器節點下新增一個網格實例以方便可視化。請別忘了在世界環境中設定天空。

現在執行專案,你應該會漂浮在空間中,並能自由環顧四周。

備註

雖然傳統的關卡切換方式可用於 XR 應用(即每個關卡都重複此場景結構),但多數開發者會選擇僅建立一次 XR 場景,再以子場景方式載入各關卡。如果你確實在每個場景都複製 XR 設定,務必避免重複執行 initialize,否則依據 XR 介面不同,可能產生不可預期的結果。

在本教學系列其餘部分,我們將以單一場景製作遊戲。