如何在 Web 上构建虚拟现实
有人认为虚拟现实将在 2020 年达到 70 亿美元的价值。在那之前,web 肯定不会一直停留于 2D 环境。实际上,已经有一些简单的方法可以将虚拟现实带进浏览器了。而且那弄起来也非常有趣!
要开始你的虚拟 Web 开发之旅,有这么三种可行的方式:
- JavaScript,Three.js 和监视设备朝向
- JavaScript,Three.js 和 WebVR(我最近比较喜欢的方式)
- CSS 和 WebVR(时日尚早)
我会将每一种方式都过一遍,简要展示下它们各自是如何工作的。
JavaScript,Three.js 和监视设备朝向
当前,大多数基于浏览器的虚拟现实项目都是利用浏览器的 deviceorientation 事件。此事件告诉浏览器设备的朝向,而且允许浏览器在设备旋转或倾斜时有所动作。从虚拟现实的角度看,此功能允许你侦测到某人看向某个方向,然后你可以调整摄像头以跟随他的视线。
为了在浏览器里获得良好的 3D 场景,我们使用一个可以方便地创建 3D 形状和场景的 JavaScript 框架three.js。它封装了创建 3D 内容的大部分复杂性,让你可以专注于把你希望的东西放进你的场景。
我在 SitePoint 上写过两个使用设备朝向方法的示例:
Bringing VR to the Web with Google Cardboard and Three.js(使用谷歌 Cardboard 和Three.js实现基于Web的虚拟现实)
Visualizing a Twitter Stream in VR with Three.js and Node(使用 Three.js 和 Node 可视化Twitter 流)
如果你不熟悉 three.js 和组建场景,我推荐你看一下上面的两篇文章以更深入的理解此方法。我将只大致介绍下主要概念。
下面是要用到的关键 JavaScript 文件(你可以从上面的示例中获取,也可以 从three.js 例子下载):
- three.min.js– three.js 框架
- DeviceOrientationControls.js– 这是一个 three.js 插件,它帮助我们完成之前提到过的监视设备朝向,可以另摄像头根据设备的移动进行调整。
- OrbitControls.js– 这是一个备用的控制器,它可以在没有设备朝向事件的时候用鼠标调整摄像头。
- StereoEffect.js– 这是一个 three.js 的效果插件,可以针对每只眼睛对画面进行细微的角度调整,就像虚拟现实该有的立体效果。这使得我们不用做任何复杂的工作,就可以创造实打实的虚拟现实分屏效果。
设备朝向
使用设备朝向控制的代码大致如下:
function setOrientationControls(e) {
if (!e.alpha) {
return;
}
controls = new THREE.DeviceOrientationControls(camera, true);
controls.connect();
controls.update();
element.addEventListener('click', fullscreen, false);
window.removeEventListener('deviceorientation', setOrientationControls, true);
}
window.addEventListener('deviceorientation', setOrientationControls, true);
function fullscreen() {
if (container.requestFullscreen) {
container.requestFullscreen();
} else if (container.msRequestFullscreen) {
container.msRequestFullscreen();
} else if (container.mozRequestFullScreen) {
container.mozRequestFullScreen();
} else if (container.webkitRequestFullscreen) {
container.webkitRequestFullscreen();
}
}
在可用设备上,设备朝向事件监听器提供一组 alpha,beta 和 gamma 值。如果没有 alpha 值就不能通过设备朝向进行控制,转而替代的是轨道控制。
如果有 alpha 值,那么我们就可以使用设备朝向控制摄像头。如果用户轻击屏幕,我们还可将场景在全屏模式下呈现(我们可不想在虚拟现实下看到浏览器地址栏)。
轨道控制
如果 alpha 值不存在,我们也就不可以访问设备的定位事件,这替代了通过摄像头拖拽鼠标的技术。这看起来就像这样:
controls = new THREE.OrbitControls(camera, element);
controls.target.set(
camera.position.x,
camera.position.y,
camera.position.z
);
controls.noPan = true;
controls.noZoom = true;
主要可能混乱的代码是上面的 noPan 和 noZoom。基本上,我们不需要通过鼠标移动身体的场景,我们也不希望能够放大或缩小 —— 我只是想环视。
立体效果
为了使用立体效果,我们先这么定义:
effect = new THREE.StereoEffect(renderer);
然后再窗口重设尺寸时,相应更新它的尺寸:
effect.setSize(width, height);
在每次 requestAnimationFrame 的时候,使用效果渲染场景:
effect.render(scene, camera);
以上便是使用设备朝向方法达到虚拟现实的基本方法。这种方法对于简易的 Google Cardboard 实现效率还可以,但在 Oculus Rift 上就不是太高效了。下面要介绍的方法在 Rift 上表现得要好得多。
JavaScript,Three.js 和 WebVR
对像是 Oculus Rift 这样的头戴式虚拟设备的朝向感兴趣?WebVR 是当前可行的方法。WebVR 是一种先锋的实验性质的 JavaScript 的 API,它提供访问 Oculus Rift 和 Google Cardboard 等虚拟设备的能力。当前,它在 Firefox Nightly 和一些experimental builds of Mobile Chrome and Chromium 上可用。需要记住的一点是,WebVR 的细节依然在草稿阶段且随时可能会改变,所以可以拿它做一些实验,而且过些时候你可能需要做一些调整。
总之,WebVR API 可以让你通过如下方式访问虚拟现实设备:
navigator.getVRDevices
我不会在这里讲太多细节(在之后 SitePoint 上的文章中我会讲更多细节的东西),如果你对细节感兴趣的话可以看看 the WebVR editor’s draft。我不讲细节的原因是有更简单的方法实现 API。
上面说的更简单的实现 WebVR API 的方法是使用 WebVR Boilerplate from Boris Smus。在不同的设备上,它可以保证基本的 WebVR 功能,同时考虑到用户体验上的优雅降级。它是我至今经过的最好的 web 虚拟现实实现。如果你在找寻基于 web 的虚拟现实实现方案,这便是当前最好的选择。
要开始使用此方法,先下载 WebVR Boilerplate on Github。
你可以使用其中的文件,专心编辑 index.html,或者你也可以从草稿开始实现特定的插件。如果你想比较两者的区别,我已经把上面提过的 Visualizing a Twitter Stream in VR with Three.js and Node迁移到 WebVR powered Twitter Stream in VR 了。
要把此项目加到你自己的草稿上,你需要这些文件:
- three.min.js – 当然是我们的 three.js 框架
- VRControls.js – 一个 three.js 插件,用于通过 WebVR 进行虚拟现实的控制 (可以从样板项目找到 bower_components/threejs/examples/js/controls/VRControls.js)
- VREffect.js – 一个 three.js 插件,用于在 Oculus Rift 上显示虚拟现实效果的画面(可以从样板项目找到 bower_components/threejs/examples/js/effects/VREffect.js)
- webvr-polyfill.js – 这是一个用于对 WebVR 支持不完善的浏览器的填充工具(可以从GitHub 上找到,或者从样板项目找到 bower_components/webvr-polyfill/build/webvr-polyfill.js)
- webvr-manager.js – 这是样板代码的一部分,它管理所有的东西,包括提供进入和离开虚拟现实模式的方法(可以从 build/webvr-manager.js 找到)
实现它仅需要稍微调整下设备朝向方法。以下是给想尝试的朋友的一份综述:
控制器
VR 控制器是很容易创建的,我们只需要通过创建一个 VRControls 对象给我们之前使用的 controls 变量就可以了。由于 Boilerplate 目前应该注意浏览器没有 VR 功能,因此轨迹控制器和设备方式控制器不是必须的。这意味着你的动画应该依然在 Google Cardboard 上能够很好的运行
controls = new THREE.VRControls(camera);
VR Effect
Effect 是非常类似于实现了 StereoEffect 类的类。只不过用新的 VREffect 类替换了原来的:
effect = new THREE.VREffect(renderer);
effect.setSize(window.innerWidth, window.innerHeight);
然而,在这个方法里我们不会通过 Effect 渲染,而是,通过我们的 VR 管理器来渲染。
VR管理器
VR 管理器关注我们所有进入或退出 VR 等等诸如此类的,因此这是我们的场景最终渲染的地方。我们通过下面的方式初始化管理器:
manager = new WebVRManager(renderer, effect, {hideButton: false});
如果VR管理器在一个可兼容的浏览器里,那么它会提供一个可以让我们进入VR 模块的按钮,如果它们的浏览器不支持 VR,那么进入全屏(全屏是我们为移动端准备的)。hideButton 参数表示我们是否想隐藏按钮或不隐藏按钮,我们定义为不隐藏!
我们像这样去调用渲染,它使用的 timestamp 变量是来自 three.js 的 update() 方法:
function update(timestamp) {
controls.update();
manager.render(scene, camera, timestamp);}
在所有的地方,你都应该让 VR 根据设备的不同而转化它本身的实现。
Isrenderer.getSize() 返回一个错误?这让我抓狂了几个小时,但是要想修改这个错误——更新 three.js!
WebVR Boilerplate在行动中是什么样子的
这是我在 Twitter 上的例子的视图在支持 VR 的浏览器上的样子:
这是在 Oculus Rift 视图里当你点击 VR 图标时的样子:
这是在手机上的样子,设备方向仍然允许我们旋转场景,但是我没有截屏。一个很好的满足响应试的视图:
如果我们在手机上点击VR图标,我们就能得到为 Google Cardboard 样式设备提供的全屏视图:
CSS 和 WebVR
Mozilla 计划将虚拟现实呈现能力也加到旗下的 Firefox 浏览器 Nightly 版本中,当然当前还处于非常早起的阶段!我运气不是太好,没能在我的 Mac 和 Oculus 设备上让其工作。来自 Firefox 的 Vladimir Vukićević 承诺将 CSS 3D 变换整合进虚拟现实全屏模式。
Vladimir 的博客中有个示例,指出标记了 transform-style: preserve-3d 的元素需要为两个视角分别渲染,以达到虚拟现实:
#css-square {
position: absolute;
top: 0; left: 0;
transform-style: preserve-3d;
transform: translate(100px, 100px, 100px);
width: 250px;
height: 250px;
background: blue;}
如果你知道任何使用虚拟现实和 CSS 的示例,请告知我!我还没发现任何相关的。将 web 中的 HTML 和 CSS 用到虚拟现实,这个主意无疑是个有趣的概念。真是不可思议,web 就要进入虚拟现实的国度,我们慢慢开始接触虚拟现实设备!
总结
在接下来几年里,技术世界将缓慢但切实地拥抱虚拟现实,前提是我们的技术跟得上我们的野心!促进虚拟现实的普及与价值的内容。我们得为用户准备好可以享用的虚拟现实内容!有比 web 更好更简单的方式吗?
如果你感兴趣并且用这些代码构建了一个虚拟现实示例,请在评论中分享或者在Twitter(@thatpatrickguy)上联系我。我很乐意把它放到我的 Oculus Rift 或 Google Cardborad 上瞧一瞧!
我收集了一堆关于通过 JavaScript 实现虚拟现实和增强现实的链接,权当大家一个参考。到 Dev Diner 看看我的 Dev Diner VR and AR with JavaScript Developer Guide,它囊括了这篇文章中提及的链接,以及其它 SitePoint 上的文章等。如果你有其他不错的而我没列出的资源,也请告知我!