设置 XR

Godot 的 XR 系统简介

Godot provides a modular XR system that abstracts many of the different XR platform specifics away from the user. At the core sits the XRServer which acts as a central interface to the XR system that allows users to discover interfaces and interact with the components of the XR system.

支持的 XR 平台都实现为一个 XRInterface 类。平台支持的接口会在 XRServer 中注册,并可通过 XRServerfind_interface 方法查找到,还可通过调用该接口的 initialize 方法来初始化它。

警告

已注册的接口仅意味着该接口可用,如果主机系统不支持接口,初始化可能会失败并返回 false。不幸的是,初始化失败的原因在不同平台上各不相同:可能是因为用户没有安装所需软件,或者是根本没插入头戴设备。作为开发者,你必须对接口初始化失败的情况准备好应对措施。

由于 XR 对输出有特殊要求——尤其是头戴设备要为双眼提供不同的图像输出,Godot 中的 XRServer 将覆盖渲染系统的大部分功能。对于独立设备来说,最终输出由 XRInterface 处理,而 Godot 的常规输出系统将被禁用。对于作为第二屏幕使用的桌面 XR 设备来说,可以使用独立的 Viewport 来处理 XR 输出,从而使 Godot 的主窗口能显示其他内容。

备注

请注意,只能有一个主接口负责处理输出到 XR 设备,默认情况下它将是第一个被初始化的接口。 Godot 目前仅支持单个头戴设备的实现。不过,也有可能存在第二个接口(比如为仅支持 3DOF 的设备添加跟踪功能),但这种情况已经愈发少见。

There are three XR specific node types that you will find in nearly all XR applications:

  • XROrigin3D 在所有意图和目的上都代表了游戏空间的中心点。这是一个过于简单的说法,但我们稍后会更详细地解释。XR 平台在物理空间中跟踪的所有对象都相对于此点定位。

  • XRCamera3D 代表着在为 XR 设备渲染输出时使用的(立体)摄像机。该节点的定位由 XR 系统控制,并基于 XR 平台提供的跟踪信息自动更新。

  • XRController3D 代表玩家使用的控制器,通常有两个:左右手各一个。该节点提供对控制器上各种状态的访问,并在玩家按下按钮时发送信号。节点的定位由 XR 系统控制,并基于 XR 平台提供的跟踪信息自动更新。

还有其他与 XR 相关的节点,并且关于这三个节点还有更多内容可以讨论,具体将稍后再谈。

Which Renderer to use

Godot has 3 renderer options for projects: Compatibility, Mobile, and Forward+. The current recommendation is to use the Mobile renderer for any desktop VR project, and use the Compatibility renderer for any project running on a standalone headset like the Meta Quest 3. XR projects will run with the Forward+ renderer, but it isn't well optimized for XR right now compared to the other two.

OpenXR

OpenXR 是由 Khronos Group 维护的 XR 行业开放标准,其目的是允许不同的 XR 平台通过共用一套标准化 API,为 XR 应用程序的开发提供便利。该标准与 Godot 的利益非常吻合。

OpenXR 的 Vulkan 实现与 Vulkan 紧密集成,并接管了 Vulkan 系统的一部分。这就要求在设置 XR 系统之前,需要对 Vulkan 渲染器中的某些核心图形功能先行集成。这是将 OpenXR 包含为核心接口的主要决定因素之一。

This also means OpenXR needs to be enabled when Godot starts in order to set things up correctly. Check the Enabled setting in your project settings under XR > OpenXR.

../../_images/openxr_settings.webp

You can find several other settings related to OpenXR here as well. These can't be changed while your application is running. The default settings will get us started, but for more information on what's here see OpenXR 设置.

You'll also need to go to XR > Shaders in the project settings and check the Enabled box to enable them. Once you've done that click the Save & Restart button.

../../_images/xr_shaders.webp

警告

Many post process effects have not yet been updated to support stereoscopic rendering. Using these will have adverse effects.

设置 XR 场景

Every XR application needs at least an XROrigin3D and an XRCamera3D node. Most will have two XRController3D, one for the left hand and one for the right. Keep in mind that the camera and controller nodes should be children of the origin node. Add these nodes to a new scene and rename the controller nodes to LeftHand and RightHand, your scene should look something like this:

../../_images/xr_basic_scene.webp

The warning icons are expected and should go away after you configure the controllers. Select the left hand and set it up as follows:

../../_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(右手)。

Next we need to add a script to our root node. Add the following code into this script:

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`` 调用。

警告

正如你看到的,我们在代码中关闭了垂直同步(DisplayServer.VSYNC_DISABLED)。在使用 OpenXR 时,将渲染结果输出到一个头戴显示器(HMD)通常需要以 90Hz 或更高的频率运行。如果你的显示器是 60Hz 且开启了垂直同步,那么输出将限制在每秒 60 帧。

像 OpenXR 这样的 XR 接口会执行它们自己的同步。

同时请注意,默认情况下物理引擎以 60Hz 运行,渲染和物理帧数不一致可能会导致物理效果不流畅。你应该将 Engine.physics_ticks_per_second 设置为更高的值。

If you run your project at this point in time, everything will work but you will be in a dark world. So to finish off our starting point add a DirectionalLight3D and a WorldEnvironment node to your scene. You may wish to also add a mesh instance as a child to each controller node just to temporarily visualise them. Make sure you configure a sky in your world environment.

完成配置后运行项目,你应该会漂浮在某个空间中,并能够四处观察。

备注

While traditional level switching can definitely be used with XR applications, where this scene setup is repeated in each level, most find it easier to set this up once and loading levels as a subscene. If you do switch scenes and replicate the XR setup in each one, do make sure you do not run initialize multiple times. The effect can be unpredictable depending on the XR interface used.

在接下来的基础教程中,我们将创建一个只使用单场景的游戏作为练习。