使用有界参考空间

在 WebXR API 集中提供的各种参考空间中,**bounded-floor 参考空间** 有一些独特之处。它不仅由一个唯一的子类表示,即 XRBoundedReferenceSpace,而且它是唯一一个基于现实世界施加的限制而不是虚拟限制来限制移动的空间。本文探讨了由 XRBoundedReferenceSpace 表示的有界参考空间,描述了它们是什么以及如何使用它们。

有界参考空间有很多用途,包括虚拟绘画工作室或 3D 建造、建模或雕刻系统等项目;培训模拟或课程场景;舞蹈或其他基于表演的游戏;或者使用增强现实技术预览现实世界中的 3D 对象。

简介

有界参考空间表示一个 XR 环境,用户可以在其中在现实世界中物理移动,同时被 XR 硬件跟踪,他们的移动随后被转置到模拟中。然后,有界参考空间建立的边界表示用户现实世界环境中可用于其在模拟中移动的安全可通行、跟踪空间的边缘。

要求

因为有界参考空间在用户可以移动的范围内建立了一个有限的区域,所以它自然地限制了模拟环境的大小。如果你将用户的现实世界移动映射到虚拟环境中,那么创建大于用户可用物理空间的虚拟世界是困难的(并且可能会非常令人困惑)。想象一下,每次你迈出一步就移动 100 米会是多么不舒服!

因此,有界参考空间的要求是

  • 可以跟踪用户在现实世界中移动的 XR 硬件,例如基于摄像头的系统。
  • 有足够空间安全移动的物理空间。

基础知识

所有有界参考空间的参考空间类型都是 bounded-floor。这是目前唯一可用的有界参考空间类型;在所有其他类型中,如果你需要边界,则需要自己管理。

因为 bounded-floor 是一个基于地面的参考空间,所以用户从空间的地板上开始,考虑到现实世界的含义,这是合理的。因此,有界参考空间的原点始终将 Y=0 平面置于地板水平。然后使用二维坐标数组定义边界,仅指定 X 和 Z 分量,因为 Y 始终为 0。这些点以顺时针方向围绕房间。

请注意,如果底层平台定义了一个固定的房间规模原点和边界,它可能会将任何未初始化的值初始化为匹配该预定义信息;对于使用这些平台的用户来说,这不是意外行为。

然后,边界内的空间是用户的安全移动区域,在该区域内跟踪用户并将其移动复制到虚拟世界中。尽管用户的 XR 系统可能会提供针对退出安全区域的自动检测和保护,但始终建议自己处理此问题,观察用户位置与世界边界的碰撞,并提供指导以返回原点,或者至少保持在安全区域内。

没有定义固有边界的 XR 硬件可能支持也可能不支持有界参考空间。如果支持,它可能有一个系统允许用户指定或选择要应用的边界(如果要使用有界空间)。但是,设备完全有可能根本拒绝支持有界空间,因此你应该准备回退到其他类型的参考空间。

理解边界

如前所述,边界定义为位于地板水平的一组点,每个点定义边界区域的一个角,以顺时针方向围绕原点。下图演示了这一点。

Diagram showing how a bounded space's boundary is defined

此图定义了房间的边界,原点位于中心(按要求),以及一组 12 个点,表示可用物理空间的顶点。房间中有两个切口区域,可能代表用户身后的沙发、沙发或长凳,以及放置电脑或其他硬件的支架或桌子。正如这所暗示的那样,安全区域不需要是凸形的,但可以有任意数量的凹痕或凸起,只要它是一个连续的形状。

请注意,此处的原点坐标 (0, 0) 表示边界定义在地板水平,并且本质上是地板上的二维形状,就像用于防止宠物远离家的无形围栏一样。此处的完整坐标将是 (0, 0, 0)。

此边界保存在 XRBoundedReferenceSpaceXRBoundedReferenceSpace 属性 boundsGeometry 中。此属性包含一个 DOMPointReadOnly 对象数组,每个对象定义构成空间边界的点之一,以顺时针方向围绕房间移动。数组中的每个顶点的 y 坐标都为 0,因为整个边界定义在地板水平,向上延伸到天花板或无限远。每个点的 w 也始终为 1。

有界区域的内部始终被认为位于边界的右侧。通过以顺时针顺序列出点,边界被放置在定义的形状内部。如果点以逆时针顺序列出,则表明安全区域位于边界的外部,可能会产生不良结果。

你应该考虑包含对用户接近边界进行主动检查。这对于他们的安全(如果边界代表某种物理障碍)以及避免边界附近精度降低的可能情况都很有用。它也很有用,因为用户可能会专注于游戏或其他活动,没有意识到他们正在接近边界,并且如果他们漫游到跟踪范围之外(尤其是在这样做会导致他们输掉游戏时),可能会感到困惑或不安。

最简单的解决方案是将每个边界段都视为要对其进行命中测试的对象。当用户靠近边界时,你可能会通过显示消息、闪烁警告指示器、播放音频警告等方式警告他们。如果用户实际与边界发生碰撞,则不要让他们继续越过边界。

创建有界参考空间

在创建依赖于有界参考空间的项目之前,务必记住,并非所有 XR 设备都能够创建它们。本质上,有界参考空间具有特殊的硬件要求,因为它们需要允许用户在空间中物理移动,同时跟踪他们的移动。在本节中,我们将了解如何在支持或不支持有界空间的情况下安全地创建会话。

安全创建首选有界空间

在实际尝试创建有界参考空间之前,你需要创建一个支持它们的会话。由于并非所有硬件都支持有界参考空间,因此你应该确保支持有界参考空间作为选项,而不是作为必需功能,除非你对代码将运行的环境有特殊了解。你可以使用以下代码创建支持 bounded-floor 参考空间(如果可用)的会话

js
async function onActivateXRButton(event) {
  if (!xrSession) {
    navigator.xr
      .requestSession("immersive-vr", {
        requiredFeatures: ["local-floor"],
        optionalFeatures: ["bounded-floor"],
      })
      .then((session) => {
        xrSession = session;
        startSessionAnimation();
      });
  }
}

此函数在用户单击按钮以启动 XR 体验时被调用,如果会话已存在,则照常退出,然后使用 immersive-vr 模式请求新会话。请求会话时指定的选项指示会话必须至少与 local-floor 参考空间兼容,但如果 bounded-floor 空间也受支持,那就更好了。

创建会话后,我们的 startSessionAnimation() 函数可以尝试建立一个 bounded-floor 参考空间,如果失败,则可以回退到请求 local-floor 参考空间(在这种情况下,我们将必须自己处理边界)。

这样,无论用户的平台是否能够提供有界参考空间,我们的会话都会启动。

创建参考空间

在调用 XRSystem 方法 requestSession() 时,仅请求 bounded-floor 支持不足以获得有界空间。您需要在调用 requestReferenceSpace() 时也请求它。这意味着您需要更改调用 requestReferenceSpace() 的代码,以请求有界参考空间,如果失败则回退到您的备用选择,如下所示

js
let xrSession = null;
let xrReferenceSpace = null;
let spaceType = null;

function onSessionStarted(session) {
  xrSession = session;

  spaceType = "bounded-floor";
  xrSession
    .requestReferenceSpace(spaceType)
    .then(onRefSpaceCreated)
    .catch(() => {
      spaceType = "local-floor";
      xrSession
        .requestReferenceSpace(spaceType)
        .then(onRefSpaceCreated)
        .catch(handleError);
    });
}

function onRefSpaceCreated(refSpace) {
  xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(xrSession, gl),
  });

  // Now set up matrices, create a secondary reference space to
  // transform the viewer's pose, and so forth.

  xrSession.requestAnimationFrame(onDrawFrame);
}

如果您将此代码与使用无界参考空间的示例中使用的代码进行比较,您将确认,实际上,最大的区别在于参考空间类型 bounded-floor

代码首先尝试获取 bounded-floor 参考空间,但如果失败,则请求 local-floor 空间。无论哪种情况,成功获取参考空间都会将新空间传递给函数 onRefSpaceCreated()。如果两种类型的空间都无法创建,则会调用错误处理程序 (handleError())。

无论哪种情况,一旦创建了参考空间,它就会传递给名为 onRefSpaceCreated() 的函数,该函数接管设置空间以供使用。

但是,务必记住,虽然 local-floor 空间提供了一个相对于地面的空间,并且始终可用于沉浸式会话,但它与 bounded-floor 也有很大的区别,因此您需要做好处理这些差异的准备。这就是上面代码片段中将使用的参考空间记录在变量 spaceType 中的原因。最明显的区别是 local-floor 空间不提供边界,并且主要用于用户在会话期间停留在一个位置的情况。

如果在尝试创建 local-floor 参考空间时,用户的 XR 设备没有内置支持来确定地面水平,则 WebXR 层仍将创建一个 local-floor 空间。但是,地面水平将通过选择和模拟地面水平并将视图向上移动固定量来模拟,以确保场景的内容在正确的位置渲染。

请记住,默认情况下,观察者的位置放置在地面的正上方,就像放在地上的摄像机一样。如果您希望模拟人类对场景的视角,您可能希望通过变换它来提供适当的变换矩阵到 XRReferenceSpace 方法 getOffsetReferenceSpace(),从而将视点向上移动一段距离,以近似人类眼睛的高度。

这将更改上面代码片段中的 onRefSpaceCreated() 方法为

js
function onRefSpaceCreated(refSpace) {
  xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(xrSession, gl),
  });

  let startPosition = vec3.fromValues(0, 1.5, 0);
  const startOrientation = vec3.fromValues(0, 0, 1.0);
  xrReferenceSpace = xrReferenceSpace.getOffsetReferenceSpace(
    new XRRigidTransform(startPosition, startOrientation),
  );

  xrSession.requestAnimationFrame(onDrawFrame);
}

在此代码中,在创建参考空间后执行,我们创建一个 XRRigidTransform,表示将视点向上移动 1.5 米的变换。这近似于人的身高,尽管它假设我们之前已经变换了坐标系,以便每个坐标的值不再限制在 -1 到 1 之间,同时保持 1 的值表示 1 米的定义。

新的变换传递到 getOffsetReferenceSpace() 以获取一个参考空间,该空间映射基础坐标系和渲染图像的坐标系之间的坐标。新的参考空间替换了原来的参考空间。最后,通过调用 XRSession 方法 requestAnimationFrame() 开始绘制。

另请参阅