以下是一些基础知识,可帮助您为各种沉浸式体验做好准备:虚拟现实、增强现实以及介于两者之间的所有内容。
沉浸式体验在 Chrome 79 中首次登陆网络。WebXR Device API 带来了虚拟现实,而对增强现实的支持则在 Chrome 81 中推出。同时,GamePad API 的更新将控件的高级使用扩展到了 VR。其他浏览器也将很快支持这些规范,包括 Firefox Reality、Oculus Browser、Edge 和 Magic Leap 的 Helio 浏览器等。
本文是沉浸式网络系列文章的开篇。本期文章介绍了如何设置基本的 WebXR 应用,以及如何进入和退出 XR 会话。后续文章将介绍帧循环(WebXR 体验的主力)、增强现实的具体细节,以及 WebXR Hit Test API(一种在 AR 会话中检测表面的方法)。除非另有说明,否则我在本文和后续文章中介绍的所有内容均同样适用于 AR 和 VR。
什么是沉浸式网络?
虽然我们使用两个术语来描述沉浸式体验(增强现实和虚拟现实),但许多人认为它们处于从完全现实到完全虚拟的范围内,中间有不同程度的沉浸感。XR 中的“X”旨在反映这种想法,它是一种代数变量,代表沉浸式体验范围内的任何内容。
沉浸式体验的示例包括:
- 游戏
- 360° 全景视频
- 在沉浸式环境中呈现的传统 2D(或 3D)视频
- 购房
- 在购买前在家中查看产品
- 沉浸式艺术
- 一些尚未被发现的酷炫内容
概念和用法
我将介绍使用 WebXR Device API 的一些基础知识。如果您需要比我提供的更深入的信息 ,请查看 Immersive Web Working Group 的 WebXR 示例或 MDN 不断增长的 参考 资料。 如果您熟悉早期版本的 WebXR Device API,则应浏览所有这些资料。因为其中有一些更改。
本文中的代码基于 Immersive Web Working Group 的基本 示例(演示、 源代码), 但为清晰起见和简洁而进行了编辑。
创建 WebXR 规范的一部分工作是充实安全和隐私保护措施,以保护用户。因此,实现必须遵守某些要求。网页或应用必须处于活动状态并获得焦点,然后才能向观看者请求任何敏感信息。网页或应用必须通过 HTTPS 提供。API 本身旨在保护从传感器和摄像头获取的信息,而这些信息是 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 兼容的
WebGLRenderingContext
或
WebGL2RenderingContext。
所有绘制都是使用它们或基于 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 帧的速度运行。您的代码不应假定任何特定的帧速率。
帧循环的基本流程如下:
- 调用
XRSession.requestAnimationFrame()。作为响应,用户代理会调用您定义的XRFrameRequestCallback。 - 在回调函数内:
- 再次调用
XRSession.requestAnimationFrame()。 - 获取观看者的姿势。
- 将
WebGLFramebuffer从XRWebGLLayer传递(“绑定”)到WebGLRenderingContext。 - 遍历每个
XRView对象,从XRWebGLLayer中检索其XRViewport,并将其传递给WebGLRenderingContext。 - 在帧缓冲区中绘制内容。
- 再次调用
本文的其余部分介绍了第 1 步和第 2 步的一部分,即设置
和调用 XRFrameRequestCallback。第 2 步的其余项将在第 II 部分中介绍。
XRFrameRequestCallback
XRFrameRequestCallback 由您定义。它接受两个参数:DOMHighResTimeStamp 和 XRFrame 实例。XRFrame 对象提供将单个帧渲染到显示屏所需的信息。DOMHighResTimeStamp 实参供日后使用。
在执行任何其他操作之前,我将请求下一个动画帧。如前所述,帧的时序由用户代理根据底层硬件确定。先请求下一帧可确保在回调期间发生错误时,帧循环继续运行。
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
// Render a frame.
}
此时,该为观看者绘制一些内容了。这是第 II 部分要讨论的内容。在介绍该部分之前,让我先向您展示如何结束会话。
结束会话
沉浸式会话可能会因多种原因而结束,包括通过调用 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 应用所需的所有内容。 希望我提供的内容足以让您开始自行理解代码,并开始进行实验。在下一篇文章中,我将介绍帧循环,即内容绘制到屏幕上的位置。