虚拟现实即将登陆网络

只需了解一些基础知识,就能准备好各种沉浸式体验:虚拟现实、增强现实以及介于二者之间的各种体验。

Joe Medley
Joe Medley

Chrome 79 为网页引入沉浸式体验。WebXR Device API 将虚拟现实带来了虚拟现实,而 Chrome 81 则开始支持增强现实。GamePad API 的更新将控件的高级使用扩展至 VR。其他浏览器很快就会支持这些规格,包括 Firefox Reality、Oculus 浏览器、Edge 和 Magic Leap 的 Helio 浏览器等。

本文开始了关于沉浸式 Web 的系列文章。本期内容介绍了如何设置基本的 WebXR 应用以及如何进入和退出 XR 会话。后续文章将介绍帧循环(WebXR 体验的主力)、增强现实的具体细节,以及 WebXR Hit Test API(一种在 AR 会话中检测表面的方法)。除非另有说明,否则我在本文和后续文章中介绍的所有内容均适用于 AR 和 VR。

什么是沉浸式网络?

虽然我们使用两个术语来描述沉浸式体验,即增强现实和虚拟现实,但许多人认为它们从完全现实到完全虚拟,中间有一定的沉浸感。XR 中的“X”旨在通过作为一种代数变量来代表沉浸式体验中的任何内容,从而反映这种思维方式。

一张图,显示了从完全现实到完全沉浸式的一系列视觉体验。
各种各样的沉浸式体验

沉浸式体验的示例包括:

  • 游戏
  • 360° 全景视频
  • 在沉浸式环境中呈现的传统 2D(或 3D)视频
  • 买房
  • 先在家中浏览产品,然后再决定是否购买
  • 沉浸式艺术
  • 还没有人想到的酷炫功能

概念和用法

我会说明使用 WebXR Device API 的一些基础知识。如果您还想了解更多内容,请查看 Immersive Web 工作组的 WebXR 示例MDN 不断增加的参考资料。 如果您熟悉 WebXR Device API 的早期版本,应该浏览一下所有资料。已经更改过。

本文中的代码基于沉浸式 Web 工作组的准系统示例(演示源代码),但为清楚起见,我们对其进行了编辑。

制定 WebXR 规范的一部分一直是充实的安全和隐私措施,以保护用户。因此,实现必须遵循某些要求。网页或应用必须处于活跃状态并处于聚焦状态,才能请求浏览者提供任何敏感内容。网页或应用必须通过 HTTPS 提供。此 API 本身的设计是保护从传感器和相机获取的信息,而这些信息是正常运行所需的。

申请会话

进入 XR 会话需要用户手势。为此,请使用功能检测来测试 XRSystem(通过 navigator.xr)并调用 XRSystem.isSessionSupported()。请注意,在 Chrome 版本 79 和 80 中,XRSystem 对象称为 XR

在下面的示例中,我已经表示我需要一个会话类型为 'immersive-vr' 的虚拟现实会话。其他会话类型'immersive-ar''inline'。内联会话用于在 HTML 中呈现内容,主要用于宣传片内容。沉浸式 AR 会话示例演示了这一点。我们将在后续文章中对此进行介绍。

确定系统支持虚拟现实会话后,我会启用一个按钮,让我能够获取用户手势。

if (navigator.xr) {
  const supported = await navigator.xr.isSessionSupported('immersive-vr');
  if (supported) {
    xrButton.addEventListener('click', onButtonClicked);
    xrButton.textContent = 'Enter VR';
    xrButton.enabled = supported; // supported is Boolean
  }
}

启用该按钮后,我会等待点击事件,然后请求会话。

let xrSession = null;
function onButtonClicked() {
  if (!xrSession) {
    navigator.xr.requestSession('immersive-vr')
    .then((session) => {
      xrSession = session;
      xrButton.textContent = 'Exit XR';
      onSessionStarted(xrSession);
    });
  } else {
    xrSession.end();
  }
}

请注意此代码中的对象层次结构。它会从 navigator 移至 xr,然后再移至 XRSession 实例。在早期版本的 API 中,脚本必须先请求设备,然后才能请求会话。现在,设备是隐式获取的。

进入会话

获取会话后,我需要启动并进入会话。但首先,我需要进行一些设置会话需要一个 onend 事件处理脚本,以便在用户退出时重置应用或网页。

我还需要一个 <canvas> 元素来绘制场景。它必须是与 XR 兼容的 WebGLRenderingContextWebGL2RenderingContext。 所有绘制都是使用它们或基于 WebGL 的框架(例如 Three.js)完成的。

现在有了用于绘制的地方,我需要一个内容来源,以便在上面进行绘制。为此,我创建了一个 XRWebGLLayer 实例。我通过调用 XRSession.updateRenderState() 将其与画布相关联。

进入会议后,我需要一种方法来确定事物处于虚拟现实的哪个位置。我需要一个参考空间。'local-floor' 参考空间是指原点位于观看器附近,y 轴在楼层为 0 的位置,预计不会移动。还有其他类型的引用空间,但这是一个更复杂的主题,我无法在这里展开讨论。我将引用空间保存到变量中,因为在屏幕上进行绘制时需要用到它。

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  let canvas = document.createElement('canvas');
  webGLRenContext = canvas.getContext('webgl', { xrCompatible: true });

  xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(xrSession, webGLRenContext)
  });

  xrSession.requestReferenceSpace('local-floor')
  .then((refSpace) => {
    xrRefSpace = refSpace;
    xrSession.requestAnimationFrame(onXRFrame);
  });
}

获得引用空间后,我调用 XRSession.requestAnimationFrame()。这是呈现虚拟内容的开始,在帧循环中完成。

运行帧循环

帧循环是一种由用户代理控制的无限循环,其中内容会反复绘制到屏幕上。内容是在称为“帧”的离散块中绘制的。这一系列画面营造出运动的错觉效果。对于 VR 应用,每秒帧数介于 60 到 144 之间。Android 版 AR 的运行速度为每秒 30 帧。您的代码不应假设任何特定的帧速率。

帧循环的基本流程如下:

  1. 调用 XRSession.requestAnimationFrame()。作为响应,用户代理会调用您定义的 XRFrameRequestCallback
  2. 在您的回调函数内:
    1. 再次调用 XRSession.requestAnimationFrame()
    2. 摆好观看者的姿势。
    3. WebGLFramebufferXRWebGLLayer 传递(“绑定”)到 WebGLRenderingContext
    4. 遍历每个 XRView 对象,从 XRWebGLLayer 中检索其 XRViewport 并将其传递给 WebGLRenderingContext
    5. 向帧缓冲区绘制内容。

本文的其余部分将介绍第 1 步以及第 2 步(设置和调用 XRFrameRequestCallback)的一部分。第二部分将介绍第 2 步的其余内容。

XRFrameRequestCallback

XRFrameRequestCallback 由您定义。它需要两个参数:DOMHighResTimeStampXRFrame 实例。XRFrame 对象提供将单个帧渲染到显示屏所需的信息。DOMHighResTimeStamp 参数是供将来使用。

在执行任何其他操作之前,我将请求下一个动画帧。如前所述,帧时间由用户代理根据底层硬件确定。先请求下一帧可确保在回调期间的某些内容抛出错误时,帧循环会继续进行。

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  // Render a frame.
}

此时,该为观看者绘制一些内容了。这就是第二部分讨论的内容在前往此页面之前,我先来展示如何结束会话。

结束会话

沉浸式会话可能因多种原因而结束,包括通过调用 XRSession.end() 以您自己的代码结束。其他原因包括耳机断开连接或由其他应用控制耳机。因此,行为良好的应用应监控 end 事件。出现这种情况时,请舍弃该会话及其相关的渲染对象。已结束的沉浸式会话无法恢复。为了重新进入沉浸式体验,我的应用需要启动一个新会话。

回想一下进入会话一文中所述的内容,在设置过程中,我添加了 onend 事件处理脚本。

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);
  // More setup…
}

在事件处理脚本内,恢复用户进入会话之前应用的状态。

function onSessionEnded(event) {
  xrSession = null;
  xrButton.textContent = 'Enter VR';
}

总结

我还没有说明编写 Web XR 或 AR 应用所需的所有内容。 希望我给了大家足够的资源,让自己可以开始理解代码,也足够您开始实验了。在下一篇文章中,我将说明帧循环,即内容绘制到屏幕的位置。

照片由 JESHOOTS.COM 提供,由 Unsplash 提供