WebXR 中的几何图形和参考空间

从根本上讲,无论是在增强现实还是虚拟现实环境中,WebXR呈现场景的渲染都是使用WebGL执行的,因此这两个 API 共享许多相同的语言设计。但是,为了能够使用 XR 头显和其他此类设备以真正的 3D 方式呈现场景,WebXR 有一些额外的概念需要理解。

在本文中,我们将介绍 WebXR 如何扩展 WebGL 的几何图形,以及如何使用空间,特别是参考空间,来描述对象(物理对象和虚拟对象)彼此之间的位置和方向。

文章WebXR 中的空间跟踪在此提供的信息基础上,介绍了如何将用户的头部以及可能的身体其他部位(例如手)的物理位置和方向映射到数字世界中,以及如何跟踪物理对象和虚拟对象在移动时的相对位置,以便正确渲染和合成场景。

3D 几何图形基础

虽然我们将在此处检查用于计算虚拟空间中对象的位置、方向和移动所需的数学运算,以及将场景的人类观察者整合到其中的必要性,但对几何图形以及使用矩阵和向量管理场景 3D 表示的全面介绍超出了本文的范围。您可以在Web 的矩阵数学中了解更多关于各个运算的信息。

单位

在讨论 WebXR 使用的 3D 空间几何图形的细节之前,首先了解应用于 3D 世界的度量单位非常有用。

长度和距离

WebGL 以**米**为单位测量所有距离和长度。WebXR 继承了此标准,以及世界是一个宽 2 米、高 2 米、深 2 米的立方体的事实。三个轴中的每一个的最小值为 -1.0,最大值为 1.0,立方体的中心位于 (0, 0, 0)。

Diagram showing a WebXR space whose X, Y, and Z coordinate axes each have a minimum value of -1 and a maximum of 1.

对于您的代码而言,这 8 立方米的空间包含了整个宇宙。您绘制的所有内容都必须将其坐标映射到此空间中,这可以通过在您的代码中显式设置或使用变换调整所有顶点的坐标来实现。当然,最有效的方法是设计您的对象和代码以使用与 WebGL 相同的坐标系。

WebGL 坐标和长度在渲染时会自动转换为渲染场景的视口的大小。

角度

角度使用**弧度**指定。要将度数转换为弧度,请将度数乘以 π/180。以下代码片段显示了两个简单的函数,degreesToRadians()radiansToDegrees(),它们在用于测量角度的两个单位之间进行转换。

js
const RADIANS_PER_DEGREE = Math.PI / 180.0;

let degreesToRadians = (deg) => deg * RADIANS_PER_DEGREE;
let radiansToDegrees = (rad) => rad / RADIANS_PER_DEGREE;

时间和持续时间

注意:出于安全原因,DOMHighResTimeStamp 通常会向时钟引入少量不精确性,以防止其用于指纹识别和基于时间的攻击。

WebXR 中的所有时间和持续时间都使用DOMHighResTimeStamp类型进行测量,该类型是一个双精度浮点数,指定相对于起始时间以毫秒为单位的时间。由于该值是浮点数,因此根据平台和硬件,它可能会比毫秒级别精确得多。

时间主要用于确定自场景的前一动画帧绘制以来经过的时间。因此,时间通常与显示器的刷新率保持一致,或者如果由于性能问题需要限制帧率,则为其某个分数。这意味着,假设帧率为 60 FPS,则时间通常会以 1/60 秒的间隔递增。进行计算,我们发现这意味着每个帧的理想渲染间隔为 16.6667 毫秒。

使用矩阵进行几何运算

我们提供了一个关于与 3D 几何图形相关的矩阵数学的指南,包括在渲染 3D 场景时需要执行的三种主要变换如何使用矩阵。

  • 平移是使用矩阵通过虚拟空间移动点的位置。此运动可以沿着对象的任何轴或任何组合进行。
  • 旋转是应用一个矩阵,该矩阵围绕对象坐标系的原点旋转一个点。
  • 缩放是使用矩阵更改对象的大小。

请注意,当我们说变换应用于一个点时,它也可以扩展应用于点集合。由于对象由一定数量的多边形组成,而多边形又由空间中一定数量的点组成,因此对构成对象的每个点应用相同的变换将对整个对象应用相同的变换。变换也可以应用于向量,因为向量使用坐标值来定义向量的方向和大小。

关于空间的起源

一个完整的 XR 增强场景(无论是虚拟的还是增强的)都由一个到几十个参考帧的组合组成。场景中每个需要与 WebXR 系统直接交换位置和方向数据的对象都需要能够以一种可以理解和根据需要调整的方式报告这些信息,以便其他场景中的对象能够理解。

在增强现实 (AR) 中,这是因为需要将虚拟对象插入到现实世界中,不仅要正确放置它们,还要确保在用户视角发生变化时,它们不会自行四处游荡。在虚拟现实 (VR) 中,这一切都是为了创造一种空间感,在该空间中,用户的移动与虚拟显示器上呈现的图像精确匹配,以防止可能导致不适或更糟情况的分离和断开连接。

因此,这一切都与创造空间感有关。从 XR 开发人员的角度来看,设计舞台对您的用户来说是最重要的部分。就像建筑师或布景设计师一样,您有能力通过物理环境来创造情绪和体验。您构建空间的方式将取决于并影响用户如何交互和探索它。

注意:空间通常具有前景、中景和背景元素。正确的平衡可以创造独特的存在感并引导您的用户。前景包括您可以直接交互的对象和界面。中景包括您可以一定程度上交互的对象,或者您可以接近以更仔细地检查和参与的对象。另一方面,背景通常在很大程度上或完全不可交互,至少在用户能够接近它并将其带入中景或前景范围之前是这样。

在 WebXR 中,**空间**(即场景发生的坐标空间)的基本概念由 XRSpace 的实例表示。空间用于确定用户环境中对象和其他实体(例如光源和相机)的相对位置和运动。

如前所述,任何给定的 3D 点都包含三个分量,每个分量都标识沿三个轴之一从空间中心到该点的距离。

这是空间的**原生原点**,对应于用户环境中的特定物理位置。每个空间都有自己的原生原点,由 XR 设备的跟踪系统跟踪。这可能与**有效原点**不同,有效原点是空间局部坐标系的原点。

坐标系的方向性可以在下图中看到

Diagram showing the coordinate system used by WebGL and WebXR.

一个名为**原点偏移**的 XRRigidTransform 用于将点从空间自己的有效坐标系转换到 XR 设备的原生坐标系。原点偏移最初是一个单位变换,因为通常在空间首次建立时,这两个原点是对齐的。但是,随着时间的推移,对齐方式的变化累积,原点偏移可能会发生变化以进行补偿。

点在空间中相对于原点的位置是通过确定它沿上图所示的三个空间轴的距离来确定的。空间的原点是点 (0, 0, 0),位于空间的中心,并且在每个轴上的零位置。具体来说,在初始起始条件下,查看器在空间上的默认方向下

  • **x 轴**从原点水平向左向右延伸,+1.0 的 x 坐标位于世界的右侧边缘。x 的负值从原点向左延伸,在空间的左侧边缘达到 -1.0 的值。
  • **y 轴**从原点向上延伸到屏幕顶部,在世界的顶部达到 +1.0。小于 0 的 y 值位于原点下方,向屏幕底部延伸,并在世界的底部达到 -1.0。
  • **z 轴**从原点向屏幕外延伸,在该方向上最靠近用户的点达到 +1.0。z 的负值从用户向屏幕更深处延伸,世界上最远处的点具有 -1.0 的 z

从最简单的层面上讲,每个对象都是一组由 3D 空间中的点和偏移变换定义的多边形,指示如何移动和旋转对象以将其定位在空间中的所需点。如果偏移变换是单位矩阵,则对象位于原点。

但是,为了对空间跟踪和场景几何图形有用,您需要能够将 XR 设备感知到的位置与空间的坐标系相关联。这就是参考空间的用武之地。

参考空间

由于可用的 XR 硬件种类繁多,从众多开发人员那里以各种不同的外形尺寸出现,因此期望开发人员必须直接与正在使用的跟踪技术进行通信是不切实际且不可扩展的。相反,WebXR 设备 API 旨在让开发人员规划用户体验并请求最能代表这些需求的适当参考空间。这是通过向 用户代理 请求与这些需求匹配的XRReferenceSpace 来完成的。

XRReferenceSpace 对象充当将一个坐标系参考系适配到另一个坐标系参考系的方法。戴上头显后,可以认为您周围的虚拟世界具有一个坐标系,其中您的位置为 (0, 0, 0)——也就是说,您处于一切的中心。这感觉是不是很强大?向前,正对着您的头显,是 -Z 轴,+Z 在您身后。X 向右为正,向左为负。当您向下移动时,Y 为负,当您向上移动时,Y 为正。这表示头显在使用 XR 系统开始时的空间位置,原点 (0, 0, 0) 基本上位于您的鼻梁处。此空间是**世界空间**。

接下来,考虑您左手持有的 XR 控制器。它能够报告运动及其方向,但它不知道头显的位置或更重要的是其坐标系。但控制器仍然需要一种方法来向您的应用程序报告其位置。因此,它有自己的坐标系。这是一个参考空间,在发生输入事件时提供给您的应用程序。此参考空间在内部知道如何将控制器的坐标映射到头显的坐标,因此 WebXR 可以为您来回转换坐标。

创建后,XRReferenceSpace 保证一定程度的运动和方向跟踪支持,并提供了一种从 XRViewerPose 获取矩阵的机制,该矩阵表示空间相对于世界空间的位置和朝向,如果空间表示查看器(例如用户的头显、观察者的头显或虚拟相机)。

所有这些都是浏览器负责处理的,无论每个底层参考空间的功能如何,都提供一致的行为。无论单个 XR 设备多么强大或简单,使用 WebXR 编写的代码仍然可以工作,但在可用硬件的限制范围内。

无论您选择哪种类型的参考空间,其类型都是 XRReferenceSpace 或从 XRReferenceSpace 派生的类型。当前可用的参考空间类型如下所示。

bounded-floor

一个 XRBoundedReferenceSpace,类似于 local 类型,除了用户预计不会移动到返回对象中的 boundsGeometry 所给出的预定边界之外。

local

一个 XRReferenceSpace 跟踪空间,其原生原点位于会话创建时查看器位置附近。确切位置取决于底层平台和实现。用户预计不会在其起始位置之外移动太多(如果有的话),并且跟踪针对此用例进行了优化。对于具有六自由度 (6DoF) 跟踪的设备,local 参考空间试图使原点相对于环境保持稳定。

local-floor

一个 XRReferenceSpace,类似于 local 类型,除了起始位置放置在查看器可以安全站立的位置,其中 y 轴的值在地板水平处为 0。如果不知道地板水平,则 用户代理 将估计地板水平。如果估计的地板水平不为零,则浏览器预计会将其四舍五入,以避免 指纹识别(可能是最接近的厘米)。

unbounded

一个 XRReferenceSpace 跟踪空间,它允许用户完全自由地移动,可能从其原点出发移动非常长的距离。查看器根本没有被跟踪;跟踪针对用户当前位置周围的稳定性进行了优化,因此原生原点可能会根据需要漂移以满足该需求。

viewer

一个 XRReferenceSpace 跟踪空间,其原生原点跟踪查看器的位置和方向。这用于用户可以物理四处移动的环境中,并且所有 XRSession 实例(沉浸式和内联)都支持此功能,尽管它对内联会话最有用。在确定查看器和输入之间的距离或使用偏移空间时,它特别有用。否则,通常,其他参考空间类型将更常使用。

本指南的其余部分探讨了如何为应用程序的需求选择正确的参考空间。

使用参考空间定义空间关系

有多种常用的方法来参考对象相对于其环境的位置和方向,以及约束环境本身。为此,WebXR 定义了一组标准空间,称为**参考空间**,每个空间都支持不同的技术来将其局部空间的参考系坐标系与存在空间的坐标系相关联。

但是,无论使用哪种类型的参考空间,都可以使用相同的函数将坐标从空间转换为父空间。

选择参考空间类型

首先,让我们陈述决定使用哪种参考类型的过程中最简单的步骤:您最有可能使用的参考空间是 locallocal-floorunboundedbounded-floor

地板水平参考空间

名称中包含 -floor 的参考空间类型的工作方式与相应的非地板空间相同,只是它们尝试自动确保查看器位于地面或附近(但始终高于)的安全位置。这是 y 坐标始终为 0 的平面,除非另行建立地板。如果房间的地板不平整或地板高于地面的高度不同,则这些空间类型不可行,因为它们不支持头像的垂直位置变化。

主要的参考空间类型

viewer 参考空间对应于查看器在空间中的位置;它由 XRFrame 方法 getViewerPose() 返回的 XRViewerPose 使用。否则通常不会直接使用它。唯一的例外是,当在 Web 内容中内联执行 XR 场景时,您可能会使用 viewer 参考空间。

local 参考空间通常用于描述相对较小的区域,例如单个房间。它不仅在使用沉浸式会话模式(immersive-vrimmersive-ar)时始终可用,而且在请求新会话时始终包含在可选功能中;因此,由 navigator.xr.requestSession() 创建的每个会话都支持 local 参考空间类型。

要表示大面积区域(可能涉及多个房间或超出),可以使用 unbounded 参考空间类型,该类型指定对查看器的移动没有约束。如果您希望阻止用户进入某些区域,则必须自行处理。

bounded-floor 参考空间类型没有相应的非地板绑定类型。如果用户的 XR 硬件允许他们在现实世界空间中四处移动,并且您可以做到这一点,则使用 bounded-floor 参考空间可能很有用,它允许您专门定义允许和安全的区域边界。请参阅文章 使用有界参考空间 以了解有关使用有界参考空间的更多信息。

通过使用参考空间来描述对象的位置和方向,WebXR 能够标准化用于描述这些事物的数据形式,而不管底层 XR 硬件如何。然后,参考空间的配置能够为您提供正确渲染空间内容所需的视图矩阵和对象姿态。

建立参考空间

最顶层的空间——通过调用 XRSession 方法 requestReferenceSpace() 获得的空间——描述了用于整体世界空间的坐标系。所有内容从根本上都与该坐标系相关联,该坐标系表示用户设备位置与虚拟世界之间的关系。

虽然您可以将 WebXR 用于从用注释增强世界到 360° 视频播放,再到科学模拟、虚拟现实训练系统或任何您可以想象的任何其他用途,但让我们以 3D 电子游戏为例,作为典型的 WebXR 应用程序。考虑一下游戏世界空间中站立的玩家化身模型。您可以使用世界参考空间定义的坐标系,将该化身相对于世界空间定位。

要将玩家移动到新位置,您可以重写其所有坐标或在每次移动时手动应用变换,但由于参考空间及其相互创建的能力,有一种更简单的方法。创建一个 XRRigidTransform 对象,表示玩家化身的新位置和方向,然后使用 XRReferenceSpace 方法 getOffsetReferenceSpace() 创建一个新的参考空间来表示化身在新位置的视角。在实现对使用非 XR 设备(例如键盘或鼠标)在世界中移动玩家化身功能的支持时,这一点尤其方便。

使用新创建的参考空间,化身可以保持在相同的坐标处,但在世界上看起来位于(并从其新位置的视角查看世界)。有关如何使用参考空间管理玩家视角的更详细说明,请参阅文章

在我们的游戏化身示例中,化身(或任何其他移动的生物或机器)很少是一个在世界中四处滑动的简单斑点。它们通常具有额外的形态,以及内部运动,例如移动的腿、行走时摆动的胳膊、转动或摆动的头部、四处移动的武器等等。使用标准 WebGL 技术和定位矩阵或 XRRigidTransform 将这些对象移至相对于有效原点的正确位置,从而使它们栩栩如生。

参考空间的设备限制

尽管 API 尽力弥补任何缺失的功能,但某些 XR 设备无法设置为支持给定的体验。例如,基本的耳机(如 GearVR 设备)无法在需要支持用户通过跟踪其现实世界动作在环境中走动的应用中使用。

为了支持渐进增强——从而扩大您的应用或网站的可用性——您应该选择提供所需最低功能的参考空间,或者提供一种回退机制,该机制检测获取参考空间的失败尝试并尝试使用功能较弱的替代方案。

出现的兼容性问题可能像在仅限 VR 的耳机上无法支持 immersive-ar 模式(增强现实会话)一样基本,或者可能涉及在尝试创建 XR 会话时无法满足的一个或多个必需选项的请求。

XR 会话是使用 navigator.xr.requestSession() 方法创建的。其可选参数之一是您可以使用的一个对象,用于指定会话必须(或理想情况下应该)支持的必需和/或可选功能。目前,唯一支持的选项是识别标准参考空间的字符串。使用这些字符串,您可以在代码甚至运行之前确保您能够访问可以支持您需要或偏好的参考空间类型的 WebXR 会话。

注意:目前,在创建 XRSession 时,唯一可用的选项是使用或首选的参考空间。将来,可能会提供更多选项。

对象的位置和方向

在您的应用和 WebXR API 之间交换的所有空间(位置、方向和移动)信息都相对于渲染帧时的特定空间表达。任何进一步的位置和方向管理都由您和 WebGL 处理,尽管您确实使用参考空间的原点偏移来正确地将对象放置在 3D 世界中。

当需要渲染动画帧时,将调用在您调用 WebXR 会话的 XRSession 对象的 requestAnimationFrame() 方法时指定的回调函数。回调函数接收其参数之一,即指示帧发生时间的 timestamp,并应执行对应动画帧的所有渲染操作。

随着回调函数被重复调用并带有递增的时间值,回调函数生成一系列帧,这些帧使用 XR 硬件呈现,从而向用户显示 3D 场景。

您可以在文章 渲染和 WebXR 帧动画回调 中了解有关动画过程的更多信息。

有关如何在虚拟空间中定位、定向和移动对象的示例和更详细的代码级解释,请参阅文章 移动、方向和运动

另请参阅