設定 XR
Godot XR 系統簡介
Godot 提供模組化的 XR 系統,將各種 XR 平台的細節抽象化,讓使用者不需直接面對。核心元件為 XRServer,它作為 XR 系統的中央介面,讓使用者可以探索介面並與 XR 系統的各元件互動。
每個支援的 XR 平台皆以 XRInterface 實作。支援的介面會自行向 XRServer 註冊,並可透過 XRServer 的 find_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 已勾選。
此處還有其他與 OpenXR 相關的設定。這些設定在應用程式執行時無法更改。預設值已可正常啟用,如需詳細說明,請參閱 OpenXR 設定。
另外,請到專案設定中的 XR > Shaders,勾選 Enabled,以啟用著色器。完成後,請點擊 儲存並重啟 按鈕。
警告
許多後製(後處理)特效尚未更新以支援立體算繪。使用這些效果會造成顯示異常。
建立 XR 場景
每個 XR 應用程式至少需要 XROrigin3D 與 XRCamera3D 節點。大多數情況下還會有兩個 XRController3D,分別代表左手與右手。請注意,相機與控制器節點應為原點節點的子節點。請將這些節點加入新場景,並將控制器節點分別命名為 LeftHand 與 RightHand,場景結構大致如下:
警告圖示是預期的,設定控制器後即可消失。請選取左手節點並設定如下:
右手設定如下:
目前這些節點都在地板上,執行時會自動定位。為方便開發,可將相機向上移動,令其 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")
using Godot;
public partial class MyNode3D : Node3D
{
private XRInterface _xrInterface;
public override void _Ready()
{
_xrInterface = XRServer.FindInterface("OpenXR");
if(_xrInterface != null && _xrInterface.IsInitialized())
{
GD.Print("OpenXR initialized successfully");
// Turn off v-sync!
DisplayServer.WindowSetVsyncMode(DisplayServer.VSyncMode.Disabled);
// Change our main viewport to output to the HMD
GetViewport().UseXR = true;
}
else
{
GD.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 設為更高的值。
若此時直接運作專案,雖然一切正常,但你會處於一個黑暗世界。因此,請在場景中加入 DirectionalLight3D 與 WorldEnvironment 節點。你也可以暫時在每個控制器節點下新增一個網格實例以方便可視化。請別忘了在世界環境中設定天空。
現在執行專案,你應該會漂浮在空間中,並能自由環顧四周。
備註
雖然傳統的關卡切換方式可用於 XR 應用(即每個關卡都重複此場景結構),但多數開發者會選擇僅建立一次 XR 場景,再以子場景方式載入各關卡。如果你確實在每個場景都複製 XR 設定,務必避免重複執行 initialize,否則依據 XR 介面不同,可能產生不可預期的結果。
在本教學系列其餘部分,我們將以單一場景製作遊戲。