使用有界参考空间
在 WebXR API 中可用的各种参考空间中,bounded-floor 参考空间有些独特。它不仅由一个独特的子类 XRBoundedReferenceSpace 表示,而且是唯一一个根据现实世界施加的限制而非虚拟限制来限制移动的参考空间。本文将探讨由 XRBoundedReferenceSpace 表示的有限参考空间,描述它们是什么以及如何使用它们。
有限参考空间有许多用途,包括虚拟绘画工作室或 3D 建造、建模或雕刻系统;培训模拟或课程场景;舞蹈或其他基于表演的游戏;或者使用增强现实在现实世界中预览 3D 对象等项目。
简介
有限参考空间表示一个 XR 环境,用户可以在其中在现实世界中进行物理移动,同时被 XR 硬件跟踪,其移动随后被转换到模拟中。因此,由有限参考空间建立的边界表示了用户在模拟中移动时,其现实世界环境中可安全通过、可跟踪的空间的边缘。
依赖项
因为有限参考空间建立了一个用户可以移动的有限区域,所以它自然地限制了模拟环境的大小。如果你将用户的真实世界运动映射到虚拟环境中,创建一个比用户可用物理空间更大的虚拟世界是很困难的(而且可能会相当令人困惑)。想象一下,如果你每走一步就移动 100 米,那会多么不舒服!
那么,有限参考空间的要求是:
- 能够跟踪用户在现实世界中移动的 XR 硬件,例如基于摄像头的系统。
- 有足够安全移动空间的物理空间。
基础知识
所有有限参考空间的参考空间类型都是 bounded-floor。这是目前唯一可用的有限参考空间类型;在所有其他类型中,如果需要边界,则必须自行管理。
由于 bounded-floor 是一个地面绑定的参考空间,用户从空间的地面开始,这在考虑到实际世界影响时是合理的。因此,有限参考空间的起点总是将 Y=0 平面置于地面水平。边界随后使用一个 2D 坐标数组定义,只指定 X 和 Z 分量,因为 Y 总是 0。这些点以顺时针方向围绕房间。
请注意,如果底层平台定义了固定的房间尺度原点和边界,它可能会将任何未初始化的值初始化为与预定义信息匹配;对于这些平台的用户来说,这并非意外行为。
边界内部的空间就是用户的安全移动区域,在该区域内,用户被跟踪,其移动被复制到虚拟世界中。虽然用户的 XR 系统可能会提供自动检测和防止离开安全区域的功能,但始终建议自行处理,监测用户位置与世界边界之间的碰撞,并提供指导以移回原点,或至少留在安全区域内。
没有固有边界定义的 XR 硬件可能支持也可能不支持有限参考空间。如果支持,它可能有一个系统,允许用户在使用有限空间时指定或选择要应用的边界。然而,设备完全有可能拒绝支持任何有限空间,因此您应该准备好回退到其他类型的参考空间。
理解边界
如前所述,边界被定义为位于地面水平的一组点,每个点定义边界区域的一个角,以顺时针方式围绕原点。这在下图中有所展示。
此图定义了房间的边界,原点位于中心,并有一组 12 个点代表可用物理空间的顶点。房间中有两个切出区域,可能代表用户身后的沙发、长沙发或长凳,以及放置电脑或其他硬件的支架或桌子。正如这所暗示的,安全区域不要求是凸形的,但可以有任意数量的凹陷或突出部分,只要它是一个连续的形状。
请注意,此处原点的坐标 (0, 0) 表明边界是在地面水平定义的,并且本质上是地板上的 2D 形状,就像用来防止宠物离家的隐形围栏一样。这里的完整坐标将是 (0, 0, 0)。
此边界在 XRBoundedReferenceSpace 中的 XRBoundedReferenceSpace 属性 boundsGeometry 中维护。此属性包含一个 DOMPointReadOnly 对象数组,每个对象定义构成空间边界的一个点,以顺时针顺序围绕房间移动。数组中的每个顶点都有一个 y 坐标为 0,因为整个边界是在地面水平定义的,向上延伸到天花板或无限。每个点的 w 也总是 1。
边界区域的内部始终被视为在边界的右侧。通过按顺时针顺序列出点,边界被放置在定义的形状内部。如果点以逆时针顺序列出,则表示安全区域位于边界外部,这可能会导致不良结果。
您应该考虑主动检查用户是否接近边界。这对于他们的安全(以防边界代表某种物理障碍)和避免在边界附近精度可能降低的情况都很有用。它也很有用,因为用户可能沉浸在游戏或其他活动中,没有意识到他们正在接近边界,并且如果他们超出跟踪范围(尤其是如果这样做导致他们输掉游戏),可能会感到困惑或沮丧。
最简单的解决方案是简单地将每个边界段视为一个用于碰撞检测的对象。当用户靠近边界时,您可以显示消息、闪烁警告指示器、播放音频警告等来警告他们。如果用户确实与边界发生碰撞,不要让他们继续越过它。
创建有限参考空间
在创建依赖有限参考空间的项目之前,重要的是要记住并非所有 XR 设备都能够创建它们。从本质上讲,有限参考空间具有特殊的硬件要求,因为它们需要允许用户在物理空间中移动,同时其移动被跟踪。在本节中,我们将探讨如何安全地创建一个无论是否支持有限空间都能工作的会话。
安全创建有限优先空间
在实际尝试创建有限参考空间之前,您需要创建一个支持它们的会话。由于并非所有硬件都支持有限参考空间,因此您应该确保将有限参考空间作为可选功能而不是必需功能来支持,除非您对代码将运行的环境有特殊了解。您可以通过使用以下代码创建一个会话来支持 bounded-floor 参考空间(如果可用):
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() 的代码以请求有限参考空间,如果失败则回退到您的备用选择,如下所示:
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() 方法更改为:
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 表示一米的定义)。
新的变换被传递给 getOffsetReferenceSpace() 以获取一个参考空间,该空间映射基本坐标系和渲染图像的坐标之间的关系。新的参考空间替换了原始空间。最后,通过调用 XRSession 方法 requestAnimationFrame() 来开始绘图。