沉浸式网络是指通过浏览器托管的虚拟世界体验。整个虚拟现实体验在浏览器或支持 VR 的头戴设备中呈现。
沉浸式网络是指通过浏览器托管的虚拟世界体验。这涵盖了在浏览器或支持 VR 的头戴设备(例如 Google 的 Daydream、Oculus Rift、Samsung Gear VR、HTC Vive 和 Windows 混合现实头戴设备)中呈现的整个虚拟现实 (VR) 体验,以及为支持 AR 的移动设备开发的增强现实体验。
虽然我们使用两个术语来描述沉浸式体验,但它们应该被视为从完全现实到完全沉浸式 VR 环境的一个连续体,中间有各种级别的 AR。
沉浸式体验的示例包括:
- 沉浸式 360° 全景视频
- 在沉浸式环境中呈现的传统 2D(或 3D)视频
- 数据可视化图表
- 家居购物
- 艺术
- 一些没人想到的酷炫创意
去那里该怎么走?
沉浸式网络已经以雏形形式存在了将近一年。 这是通过 WebVR 1.1 API 完成的,该 API 自 Chrome 62 起便在源试用中提供。Firefox 和 Edge 也支持该 API,Safari 则通过 polyfill 提供支持。
但现在是时候向前看了。
源试用已于 2018 年 7 月 24 日结束,该规范已被 WebXR Device API 和新的源试用取代。
WebVR 1.1 发生了什么变化?
我们从 WebVR 1.1 中学到了很多,但随着时间的推移,我们发现需要进行一些重大更改,才能支持开发者想要构建的应用类型。经验教训的完整列表太长,无法在此处详述,但其中包括:API 明确与主 JavaScript 线程相关联、开发者有太多机会设置明显错误的配置,以及常见用途(例如魔术窗口)是副作用而非有意为之的功能。(魔术窗口是一种无需头戴式设备即可查看沉浸式内容的技术,其中应用会根据设备的屏幕方向传感器呈现单个视图。)
新设计有助于简化实现并大幅提升性能。与此同时,AR 和其他应用场景也开始涌现,因此该 API 必须具有可扩展性,以便在未来支持这些应用场景。
WebXR Device API 在设计和命名时就考虑到了这些扩展的应用场景,并提供了更好的发展路径。WebVR 的实现者已承诺迁移到 WebXR Device API。
什么是 WebXR Device API?
与之前的 WebVR 规范一样,WebXR Device API 是沉浸式 Web 社区群组的产品,该群组有来自 Google、Microsoft、Mozilla 等公司的贡献者。XR 中的“X”是一种代数变量,代表沉浸式体验中的任何内容。该 API 可在前面提到的源试用中使用,也可通过 polyfill 使用。
当本文最初在 Chrome 67 Beta 版期间发布时,仅启用了 VR 功能。Chrome 69 引入了增强现实功能。如需了解详情,请参阅适用于 Web 的增强现实。
这个新 API 的功能非常丰富,一篇文章无法详尽介绍。我想为您提供足够的信息,让您开始了解 WebXR 示例。如需了解详情,请参阅原始说明和我们的沉浸式 Web 尝鲜者指南。随着源试用的推进,我将扩展后者。欢迎随时提出问题或提交拉取请求。
在本文中,我将讨论如何启动、停止和运行 XR 会话,以及一些有关处理输入的基础知识。
我不会介绍如何将 AR/VR 内容绘制到屏幕上。WebXR Device API 不提供图片渲染功能。决定权在您手上。绘制是使用 WebGL API 完成的。如果你真的雄心勃勃,可以这样做。 不过,我们建议您使用框架。沉浸式 Web 示例使用了一个专为演示而创建的名为 Cottontail 的框架。Three.js 自 5 月起就支持 WebXR。我没听说过 A-Frame。
启动和运行应用
基本流程如下:
- 请求 XR 设备。
- 如果可用,请请求 XR 会话。如果您希望用户将手机放入头戴式设备中,则称为沉浸式会话,需要用户手势才能进入。
- 使用会话运行渲染循环,该循环每秒提供 60 个图像帧。在每个帧中将适当的内容绘制到屏幕上。
- 运行渲染循环,直到用户决定退出。
- 结束 XR 会话。
我们来详细了解一下,并加入一些代码。您无法通过我即将展示的内容运行应用。但同样,这只是为了让大家有个概念。
请求 XR 设备
在这里,您会看到标准的特征检测代码。您可以将其封装在名为 checkForXR() 的函数中。
如果您不使用沉浸式会话,则可以跳过宣传功能和获取用户手势,直接请求会话。沉浸式会话需要使用头戴设备。非沉浸式会话只是在设备屏幕上显示内容。当您提到虚拟现实或增强现实时,大多数人都会想到前者。后者有时称为“魔术窗口”。
if (navigator.xr) {
navigator.xr.requestDevice()
.then(xrDevice => {
// Advertise the AR/VR functionality to get a user gesture.
})
.catch(err => {
if (err.name === 'NotFoundError') {
// No XRDevices available.
console.error('No XR devices available:', err);
} else {
// An error occurred while requesting an XRDevice.
console.error('Requesting XR device failed:', err);
}
})
} else{
console.log("This browser does not support the WebXR API.");
}
请求 XR 会话
现在,我们已经有了设备和用户手势,接下来需要获取会话。 如需创建会话,浏览器需要一个画布来绘制。
xrPresentationContext = htmlCanvasElement.getContext('xrpresent');
let sessionOptions = {
// The immersive option is optional for non-immersive sessions; the value
// defaults to false.
immersive: false,
outputContext: xrPresentationContext
}
xrDevice.requestSession(sessionOptions)
.then(xrSession => {
// Use a WebGL context as a base layer.
xrSession.baseLayer = new XRWebGLLayer(session, gl);
// Start the render loop
})
运行渲染循环
此步骤的代码需要一些梳理。为了理清这些概念,我将向您介绍一系列术语。如果您想查看最终代码,请跳转到后面快速浏览一下,然后返回查看完整说明。有很多信息您可能无法推断出来。
渲染循环的基本流程如下:
- 请求动画帧。
- 查询设备的位置。
- 根据设备的位置,将内容绘制到设备的位置。
- 执行输入设备所需的工作。
- 每秒重复 60 次,直到用户决定退出。
请求演示帧
在 Web XR 环境中,“帧”一词有多种含义。第一个是参考系,它定义了坐标系原点的计算位置,以及设备移动时该原点会发生什么。(当用户移动时,视图是保持不变,还是像在现实生活中一样发生变化?)
第二种类型的帧是呈现帧,由 XRFrame 对象表示。此对象包含将 AR/VR 场景的单个帧渲染到设备所需的信息。这有点令人困惑,因为呈现帧是通过调用 requestAnimationFrame() 检索的。这使其与 window.requestAnimationFrame() 兼容。
在提供更多内容之前,我先提供一些代码。以下示例展示了如何启动和维护渲染循环。请注意“帧”一词的双重用法。请注意对 requestAnimationFrame() 的递归调用。此函数每秒会被调用 60 次。
xrSession.requestFrameOfReference('eye-level')
.then(xrFrameOfRef => {
xrSession.requestAnimationFrame(onFrame(time, xrFrame) {
// The time argument is for future use and not implemented at this time.
// Process the frame.
xrFrame.session.requestAnimationFrame(onFrame);
}
});
姿势
在屏幕上绘制任何内容之前,您需要知道显示设备指向的位置,并且需要访问屏幕。通常,AR/VR 中事物的位置和方向称为姿势。观看者和输入设备都有一个姿势。(我稍后会介绍输入设备。)查看器和输入设备姿势均定义为 4x4 矩阵,并以列优先顺序存储在 Float32Array 中。您可以通过对当前动画帧对象调用 XRFrame.getDevicePose() 来获取观看者的姿势。请务必测试您是否获得了姿势。如果出现问题,您不希望在屏幕上绘制。
let pose = xrFrame.getDevicePose(xrFrameOfRef);
if (pose) {
// Draw something to the screen.
}
观看次数
检查完姿势后,就可以开始画画了。您绘制到的对象称为视图 (XRView)。此时,会话类型就变得非常重要。视图以数组形式从 XRFrame 对象中检索。如果您处于非沉浸式会话中,则该数组有一个视图。如果您处于沉浸式会话中,则数组有两个,每只眼睛各一个。
for (let view of xrFrame.views) {
// Draw something to the screen.
}
这是 WebXR 与其他沉浸式系统之间的重要区别。 虽然遍历一个视图似乎毫无意义,但这样做可让您为各种设备提供单一渲染路径。
整个渲染循环
将所有这些内容整合在一起,就得到了以下代码。我为输入设备留了一个占位符,稍后会详细介绍。
xrSession.requestFrameOfReference('eye-level')
.then(xrFrameOfRef => {
xrSession.requestAnimationFrame(onFrame(time, xrFrame) {
// The time argument is for future use and not implemented at this time.
let pose = xrFrame.getDevicePose(xrFrameOfRef);
if (pose) {
for (let view of xrFrame.views) {
// Draw something to the screen.
}
}
// Input device code will go here.
frame.session.requestAnimationFrame(onFrame);
}
}
结束 XR 会话
XR 会话可能会因多种原因而结束,包括通过调用 XRSession.end() 由您自己的代码结束。其他原因包括耳机断开连接或另一个应用控制了耳机。因此,运行良好的应用应监控结束事件,并在该事件发生时舍弃会话和渲染器对象。XR 会话一旦结束,便无法恢复。
xrDevice.requestSession(sessionOptions)
.then(xrSession => {
// Create a WebGL layer and initialize the render loop.
xrSession.addEventListener('end', onSessionEnd);
});
// Restore the page to normal after immersive access has been released.
function onSessionEnd() {
xrSession = null;
// Ending the session stops executing callbacks passed to the XRSession's
// requestAnimationFrame(). To continue rendering, use the window's
// requestAnimationFrame() function.
window.requestAnimationFrame(onDrawFrame);
}
互动是如何进行的?
与应用生命周期一样,我只是想让您体验一下如何在 AR 或 VR 中与对象互动。
WebXR Device API 采用“点击”方式进行用户输入。通过这种方法,每个输入源都有一个定义的指针光线,用于指示输入设备指向的位置,以及指示何时选择了某个内容的事件。应用会绘制指针光线并显示其指向的位置。当用户点击输入设备时,系统会触发事件,具体而言,是 select、selectStart 和 selectEnd。您的应用会确定用户点击了什么,并做出适当的响应。
输入设备和指针光线
对用户而言,指针光线只是控制器与他们所指向的对象之间的一条微弱线条。但您的应用必须绘制它。这意味着获取输入设备的姿势,并从其位置向 AR/VR 空间中的对象绘制一条线。该过程大致如下所示:
let inputSources = xrSession.getInputSources();
for (let xrInputSource of inputSources) {
let inputPose = frame.getInputPose(inputSource, xrFrameOfRef);
if (!inputPose) {
continue;
}
if (inputPose.gripMatrix) {
// Render a virtual version of the input device
// at the correct position and orientation.
}
if (inputPose.pointerMatrix) {
// Draw a ray from the gripMatrix to the pointerMatrix.
}
}
这是 Immersive Web Community Group 提供的输入跟踪示例的精简版本。与帧渲染一样,绘制指针光线和设备也由您决定。如前所述,此代码必须作为渲染循环的一部分运行。
在虚拟空间中选择内容
在 AR/VR 中,仅仅指向物体并没有多大用处。为了执行任何有用的操作,用户需要能够选择内容。WebXR Device API 提供了三个用于响应用户互动的事件:select、selectStart 和 selectEnd。它们有一个我没想到的怪癖:它们只会告诉您输入设备被点击了。它们不会告诉您点击了环境中的哪个项。事件处理脚本会添加到 XRSession 对象中,并且应在可用后立即添加。
xrDevice.requestSession(sessionOptions)
.then(xrSession => {
// Create a WebGL layer and initialize the render loop.
xrSession.addEventListener('selectstart', onSelectStart);
xrSession.addEventListener('selectend', onSelectEnd);
xrSession.addEventListener('select', onSelect);
});
此代码基于输入选择示例,以防您需要更多上下文。
如需确定点击的内容,您可以使用姿势。(您感到惊讶吗?我可不这么认为。)具体细节取决于您的应用或您使用的框架,因此不在本文的讨论范围内。Cottontail 的方法 在输入选择示例中。
function onSelect(ev) {
let inputPose = ev.frame.getInputPose(ev.inputSource, xrFrameOfRef);
if (!inputPose) {
return;
}
if (inputPose.pointerMatrix) {
// Figure out what was clicked and respond.
}
}
总结:展望未来
如前所述,增强现实功能预计将在 Chrome 69(2018 年 6 月的某个时间点推出 Canary 版)中推出。不过,我还是建议您尝试一下我们目前提供的功能。我们需要您的反馈来改进它。请在 ChromeStatus.com 上关注 WebXR 点击测试,了解其进展。您还可以关注 WebXR 锚点,这将改进姿势跟踪。