XRReferenceSpace: getOffsetReferenceSpace() 方法

可用性有限

此特性不是基线特性,因为它在一些最广泛使用的浏览器中不起作用。

安全上下文: 此功能仅在安全上下文(HTTPS)中可用,且支持此功能的浏览器数量有限。

XRReferenceSpace 接口的 getOffsetReferenceSpace() 方法返回一个新的参考空间对象,该对象描述了调用该方法的对象与 3D 空间中给定点之间的位置相对差异。getOffsetReferenceSpace() 返回的对象在以下情况时为 XRReferenceSpace:在 XRReferenceSpace 上调用;在 XRBoundedReferenceSpace 对象上调用时则返回 XRBoundedReferenceSpace

换句话说,当您在 3D 空间中有一个对象,并需要相对于该对象定位另一个对象时,可以调用 getOffsetReferenceSpace(),并将您希望第二个对象拥有的位置和方向作为参数传入,这些位置和方向是相对于调用 getOffsetReferenceSpace() 的对象的初始位置和方向

然后,在绘制场景时,您可以使用偏移参考空间来不仅定位对象之间的相对位置,还可以应用必要的变换,以根据查看者的位置正确渲染对象。这在示例 基于非 XR 输入实现旋转 中得到了演示,该示例展示了一种使用此方法让用户使用鼠标来俯仰和偏航他们的视角的方法。

语法

js
getOffsetReferenceSpace(originOffset)

参数

originOffset

一个 XRRigidTransform 对象,指定了新参考空间的源点的偏移量。这些值会加到当前参考空间的位置和方向上,然后将结果用作新创建的 XRReferenceSpace 的位置和方向。

返回值

一个与调用该方法的参考空间具有相同本地原点的 XRReferenceSpace 对象,但其原点偏移量表示从对象到 originOffset 给定点之间的距离。

如果调用此方法的对象是 XRBoundedReferenceSpace,则返回的对象也是一个 XRBoundedReferenceSpace。新参考空间的 boundsGeometry 会设置为原始对象的 boundsGeometry,其每个点的坐标都乘以 originOffset 的逆矩阵。

示例

下面是一些展示如何使用 getOffsetReferenceSpace() 的示例。

传送或设置查看者的位置

在首次创建场景时,您可能需要设置用户在 3D 世界中的位置。您可以使用 getOffsetReferenceSpace() 来实现这一点。

js
xrSession.requestReferenceSpace("local").then((refSpace) => {
  xrReferenceSpace = refSpace;
  xrReferenceSpace = xrReferenceSpace.getOffsetReferenceSpace(
    new XRRigidTransform(startPosition, { x: 0, y: 0, z: 1.0, w: 1.0 }),
  );
  xrSession.requestAnimationFrame(drawFrame);
});

在这段代码中,我们获取了一个本地参考空间,然后使用 getOffsetReferenceSpace() 创建了一个新的空间,其原点调整为 startPosition 指定的位置,并且其方向直接指向 Z 轴。然后,使用 XRSessionrequestAnimationFrame() 请求第一个动画帧。

基于非 XR 输入实现旋转

WebXR 直接支持的输入控件都是专用的 VR 或 AR 输入设备。为了使用鼠标、键盘或其他输入设备来移动或以其他方式转换 3D 空间中的对象,或者让用户在空间中移动,您需要编写一些代码来读取输入并执行相应的移动。

这是 getOffsetReferenceSpace() 的另一个很好的用例。在此示例中,我们将展示一段代码,允许用户通过右键单击并移动鼠标来更改视角,从而环顾四周。

首先,我们为 mousemove 事件添加一个事件处理程序,当按下鼠标右键时,该处理程序会调用我们的代码来执行旋转。请注意,我们还通过调用 preventDefault() 来忽略 oncontextmenu 事件。这可以防止右键单击导致浏览器显示上下文菜单。

js
canvas.oncontextmenu = (event) => {
  event.preventDefault();
};
canvas.addEventListener("mousemove", (event) => {
  if (event.buttons & 2) {
    rotateViewBy(event.movementX, event.movementY);
  }
});

接下来是 rotateViewBy() 函数,它根据 mousemove 事件中的鼠标 delta 值更新鼠标视角的偏航和俯仰。俯仰角度受到限制,以防止您看向正上方或正下方。每次调用此函数时,都会使用新的偏移量来更新 mousePitchmouseYaw 的当前值。

js
let mouseYaw = 0.0;
let mousePitch = 0.0;
const inverseOrientation = quat.create();
const MOUSE_SPEED = 0.003;

function rotateViewBy(dx, dy) {
  mouseYaw += dx * MOUSE_SPEED;
  mousePitch += dy * MOUSE_SPEED;

  if (mousePitch < -Math.PI * 0.5) {
    mousePitch = -Math.PI * 0.5;
  } else if (mousePitch > Math.PI * 0.5) {
    mousePitch = Math.PI * 0.5;
  }
}

最后,我们需要一些实际将计算出的偏航和俯仰应用到查看者方向的代码。此函数 applyMouseMovement() 负责处理。

js
function applyMouseMovement(refSpace) {
  if (!mouseYaw && !mousePitch) {
    return refSpace;
  }

  quat.identity(inverseOrientation);
  quat.rotateX(inverseOrientation, inverseOrientation, -mousePitch);
  quat.rotateY(inverseOrientation, inverseOrientation, -mouseYaw);

  let newTransform = new XRRigidTransform(
    { x: 0, y: 0, z: 0 },
    {
      x: inverseOrientation[0],
      y: inverseOrientation[1],
      z: inverseOrientation[2],
      w: inverseOrientation[3],
    },
  );

  return refSpace.getOffsetReferenceSpace(newTransform);
}

此函数从当前的俯仰和偏航值创建一个反向方向矩阵(用于定向查看者),然后使用该矩阵作为调用 XRRigidTransform() 的方向源。然后获取新的 XRRigidTransform 的参考空间并将其返回给调用者。

这个新的参考空间使得查看者的位置保持不变,但其方向已根据由累积的鼠标输入生成的俯仰和偏航值进行了更改。applyMouseMovement() 应在绘制帧时调用,紧接在通过 getViewerPose() 获取查看者姿态之前,并且渲染应在此参考空间中执行。

您可以在我们更广泛的 WebXR 教程文章 Movement, orientation, and motion 中看到类似的代码。特别是,请查看名为 Starting up the WebXR session 的部分。

规范

规范
WebXR Device API
# dom-xrreferencespace-getoffsetreferencespace

浏览器兼容性