Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
设置 XR
Godot 的 XR 系统简介
Godot 引擎内置了一套模块化扩展现实(XR)系统,该系统通过抽象化不同 XR 平台的底层实现细节,以简化跨平台 XR 应用的开发流程。该系统的核心是 XRServer 类,它作为整个 XR 架构的中枢接口,允许开发者通过该接口发现并连接各类 XR 运行时环境。
每一个受支持的 XR 平台,都是通过一个 XRInterface (XR 接口)来实现的。你可以在 here 的功能列表页面找到所有受支持的平台清单。这些受支持的接口会将自己注册到 XRServer (XR 服务器)中,你可以通过调用 XRServer 上的 find_interface 方法来查找它们。当你找到了想要的接口后,就可以通过在该接口上调用 initialize 方法来进行初始化。
警告
已注册的接口仅意味着该接口可用,如果主机系统不支持接口,初始化可能会失败并返回 false。不幸的是,初始化失败的原因在不同平台上各不相同:可能是因为用户没有安装所需软件,或者是根本没插入头戴设备。作为开发者,你必须对接口初始化失败的情况准备好应对措施。
由于 XR 对输出有特殊要求——尤其是头戴设备要为双眼提供不同的图像输出,Godot 中的 XRServer 将覆盖渲染系统的大部分功能。对于独立设备来说,最终输出由 XRInterface 处理,而 Godot 的常规输出系统将被禁用。对于作为第二屏幕使用的桌面 XR 设备来说,可以使用独立的 Viewport 来处理 XR 输出,从而使 Godot 的主窗口能显示其他内容。
备注
请注意,只能有一个主接口负责处理输出到 XR 设备,默认情况下它将是第一个被初始化的接口。 Godot 目前仅支持单个头戴设备的实现。不过,也有可能存在第二个接口(比如为仅支持 3DOF 的设备添加跟踪功能),但这种情况已经愈发少见。
在几乎所有的 XR 应用中,你都会发现三种特定于 XR 的节点类型:
XROrigin3D 在所有意图和目的上都代表了游戏空间的中心点。这是一个过于简单的说法,但我们稍后会更详细地解释。XR 平台在物理空间中跟踪的所有对象都相对于此点定位。
XRCamera3D 代表着在为 XR 设备渲染输出时使用的(立体)摄像机。该节点的定位由 XR 系统控制,并基于 XR 平台提供的跟踪信息自动更新。
XRController3D 代表玩家使用的控制器,通常有两个:左右手各一个。该节点提供对控制器上各种状态的访问,并在玩家按下按钮时发送信号。节点的定位由 XR 系统控制,并基于 XR 平台提供的跟踪信息自动更新。
还有其他与 XR 相关的节点,并且关于这三个节点还有更多内容可以讨论,具体将稍后再谈。
使用哪些渲染器
Godot 为项目提供了 3 种渲染器:兼容(Compatibility)、移动(Mobile)和 Forward+。目前的建议是,对于任何桌面 VR 项目使用移动渲染器,对于在独立头显(如 Meta Quest 3)上运行的项目使用兼容渲染器。XR 项目可以使用 Forward+ 渲染器运行,但与另外两种渲染器相比,目前它针对 XR 的优化还不够完善。
OpenXR
OpenXR 是一项全新的行业标准,它允许不同的 XR 平台通过一套标准化的 API(应用程序接口),向 XR 应用展示自己。这项标准由 Khronos 集团维护,属于开放标准,因此与 Godot 的理念(利益)非常契合。
OpenXR 的 Vulkan 实现与 Vulkan 紧密集成,并接管了 Vulkan 系统的一部分。这就要求在设置 XR 系统之前,需要对 Vulkan 渲染器中的某些核心图形功能先行集成。这是将 OpenXR 包含为核心接口的主要决定因素之一。
这也意味着需要在 Godot 启动时启用 OpenXR,以便正确设置。请检查项目设置中 XR > OpenXR 下的 Enabled 设置。
你也可以在这里找到其他几个与 OpenXR 相关的设置。不过要注意,这些选项在你的应用程序运行期间是无法修改的。目前的默认设置足够让我们起步了,但如果你想了解这里面每个选项的具体作用,可以查看 OpenXR 设置 获取更多详细信息。
你还需要前往项目设置的 XR > Shaders (XR > 着色器)选项,并勾选 Enabled (启用)复选框来激活它们。完成之后,记得点击 Save & Restart (保存并重启)按钮哦。
警告
许多后期处理特效(post process effects)目前还没有更新以支持立体渲染(stereoscopic rendering)。如果强行使用这些特效,可能会产生不良的负面影响。
设置 XR 场景
每一个 XR 应用至少都需要一个 XROrigin3D 节点和一个 XRCamera3D 节点。大多数应用还会配备两个 XRController3D 节点,分别对应左手和右手。请记住,摄像机节点和手柄控制器节点都必须是原点节点(Origin Node)的子节点。新建一个场景并添加这些节点,然后把两个手柄控制器节点分别重命名为 LeftHand 和 RightHand ,你的场景结构看起来应该像下面这样:
这些警告图标是正常的(预期会出现的),等你配置好手柄控制器后,它们就会自动消失啦。现在,请选中左手(left hand)节点,并按照下面的方式进行设置:
将脚本添加到节点:
现在,所有节点都在场景的平面上,它们将在运行时自动正确定位。为了帮助开发期间的调试,可以将相机向上移动,再把 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 调用。
警告
正如你看到的,我们在代码中关闭了垂直同步(DisplayServer.VSYNC_DISABLED)。在使用 OpenXR 时,将渲染结果输出到一个头戴显示器(HMD)通常需要以 90Hz 或更高的频率运行。如果你的显示器是 60Hz 且开启了垂直同步,那么输出将限制在每秒 60 帧。
像 OpenXR 这样的 XR 接口会执行它们自己的同步。
同时请注意,默认情况下物理引擎以 60Hz 运行,渲染和物理帧数不一致可能会导致物理效果不流畅。你应该将 Engine.physics_ticks_per_second 设置为更高的值。
如果你现在就运行项目,虽然一切功能都能正常运作,但你只会身处一片漆黑的世界里。所以,为了完善我们的起步场景,请在你的场景里添加一个 DirectionalLight3D (定向光3D)节点和一个 a WorldEnvironment (世界环境)节点。你可能还想在每个手柄控制器节点下,分别添加一个网格实例(mesh instance)作为子节点,这样就能暂时看到手柄的模型了。另外,记得一定要在你的世界环境节点里配置好天空(sky)哦。
完成配置后运行项目,你应该会漂浮在某个空间中,并能够四处观察。
备注
虽然传统的关卡切换方式确实可以用在 XR 应用中(也就是在每个关卡里都重复搭建一遍 XR 场景配置),但大多数人发现,更简单省事的做法是:只配置一次 XR 环境,然后把各个关卡作为子场景(subscene)来加载。如果你确实要切换完整场景,并且在每个场景里都复制了 XR 的配置,那请务必确保不要多次运行 initialize (初始化)函数。因为根据不同的 XR 接口,重复初始化可能会带来难以预测的后果。
在接下来的基础教程中,我们将创建一个只使用单场景的游戏作为练习。