## Web 端 VR 开发初探 文/张乾 >随着硬件和软件技术的发展,产业界对虚拟现实(Virtual Reality)用户体验产生了重大期望。技术的进步也使我们可能通过现代浏览器借助开放 Web 平台获得这种用户体验。这将帮助 Web 成为创建、分发以及帮助用户获得虚拟现实应用和服务生态系统的重要基础平台。 ### 引言 2016年最令科技界激动的话题,莫过于 VR 会如何改变世界。一些电影已开始涉足 VR,让用户不仅能看到 3D 影像,更能以“移形换影”之术身临其境,带来前所未有的沉浸式观影体验;此外,游戏领域也开始 VR 化,用户再也不用忍受游戏包里单一的场景。这些酷炫效果带来了巨大想象空间,VR 正在走近人们的生活。然而现实是,除了偶尔体验下黑科技的奇妙外,VR 并没有真正普及,在资本和硬件厂商狂热的背后,质疑声也此起彼伏。 目前,虽然 VR 硬件的发展已经走上了快车道,但内容却非常单薄。一部 VR 电影的成本相当高昂,VR 游戏也不逊色。内容创作成本的居高不下,导致了 VR 的曲高和寡。要想脱下那一层高冷的贵族华裳,飞入寻常百姓家,VR 尚需解决内容供给这一难题。以 HTML5 为代表的 Web 技术的发展,或将改变这一僵局。目前,最新的 Google Chrome 和 Mozilla Firefox 浏览器已经加入面向 HTML5 技术的 WebVR 功能支持,同时各方也正在起草并充实业界最新的 WebVR API 标准。基于 Web端的这些虚拟现实标准将进一步降低 VR 内容的技术创作成本及门槛,有利于世界上最大的开发者群体—HTML5(JavaScript)开发者进入 VR 内容创作领域。这不仅是 Web 技术发展历程上的显著突破,也为 VR 造就了借力腾飞的契机。 ### Web 端 VR 的优势 #### Web 可降低 VR 体验门槛 Web 技术不仅使创作 VR 的成本更加低廉,而且大大降低技术门槛。WebVR 依托于 WebGL 技术的高速发展,利用 GPU 执行计算以及游戏引擎技术针对芯片级的 API 优化,提高了图形渲染计算能力,大大降低开发者进入 VR 领域的门槛,同时 WebVR 还可以更好地结合云计算技术,补足 VR 终端的计算能力,加强交互体验。 可以肯定,Web 扩展了 VR 的使用范围,广告营销,全景视频等领域已经涌现一批创新案例,很多生活化的内容也纳入了 VR 的创作之中,如实景旅游、新闻报道、虚拟购物等,其内容展示、交互都可以由 HTML5 引擎轻松创建出来。这无疑给其未来发展带来更多想象空间。 #### Web 开发者基数庞大 除了技术上的实现优势,Web 还能给 VR 带来一股巨大的创新动力,因为它拥有着广泛的应用范围与庞大的开发者基数,能帮助 VR 技术打赢一场人民战争,让 VR 不再只是产业大亨们的资本游戏,而是以平民化的姿态,进入广大用户日常生活的方方面面。 相信假以时日,VR 应用会像现在满目皆是的 App 一样,大量的 VR 开发者借助于 Web 端开发的低门槛而大量进入,同时各种稀奇古怪的创意层出不穷,虚拟现实成为电商商家必须的经营手段等。若到了这个阶段,VR 离真正的繁荣也就不远了。 ### 开发 Web 端的 VR 内容 接下来我们通过实践操作来真正制作一些 Web 端的 VR 内容,体验 WebVR 的便捷优势。我们知道,许多 VR 体验是以应用程序的形式呈现的,这意味着你在体验 VR 前,必须进行搜索与下载。而 WebVR 则改变了这种形式,它将 VR 体验搬进了浏览器,Web+VR = WebVR 。在进入实践之前,下面先来分析一下 WebVR 实现的技术现状。 #### WebVR 开发的方式 在 Web 上开发 VR 应用,有下面三种方式: HTML5+ JavaScnipt + WebGL + WebVR API 传统引擎 + Emscripten[1] 第三方工具,如A-Frame[2] 第一种方法是使用 WebGL 与 WebVR API 结合,在常规 Web 端三维应用的基础上通过 API 与 VR 设备进行交互,进而得到对应的 VR 实现。第二种是在传统引擎开发内容的基础上,比如 Unity、Unreal 等,使用 Emscripten 将 C/C++ 代码移植到 JavaScnipt版本中,进而实现 Web 端的 VR 。第三种是在封装第一种方法的基础上,专门面向没有编程基础的普通用户来生产 Web 端 VR 内容。在本文中我们主要以第一和第三种方法为例进行说明。 #### WebVR 草案 WebVR 是早期和实验性的 JavaScript API,它提供了访问如 Oculus Rift、HTC Vive 以及 Google Cardboard 等 VR 设备功能的 API。VR 应用需要高精度、低延迟的接口,才能传递一个可接受的体验。而对于类似 Device Orientation Event 接口,虽然能获取浅层的 VR 输入,但这并不能为高品质的 VR 提供必要的精度要求。WebVR 提供了专门访问 VR 硬件的接口,让开发者能构建舒适的 VR 体验。 WebVR API 目前可用于安装了 Firefox nightly 的 Oculus Rift、Chrome 的实验性版本和 Samsung Gear VR 的浏览器。 #### 使用 A-Frame 开发 VR 内容 如果想以较低的门槛体验一把 WebVR 开发,那么可以使 MozVR 团队开发的 A-Frame 框架。A-Frame 是一个通过 HTML 创建 VR 体验的开源 WebVR 框架。通过该框架构建的 VR 场景能兼容智能手机、PC、 Oculus Rift和 HTC Vive。MozVR 团队开发 A-Frame 框架的的是:让构建 3D/VR 场景变得更易更快,以吸引 Web 开发社区进入 WebVR 的生态。WebVR 要成功,需要有内容。但目前只有很少一部分 WebGL 开发者,却有数以百万的 Web 开发者与设计师。A-Frame 要把 3D/VR 内容的创造权力赋予给每个人,其具有如下的优势与特点: - A-Frame 能减少冗余代码。冗余复杂的代码成为了尝鲜者的障碍,A-Frame 将复杂冗余的代码减至一行 HTML 代码,如创建场景则只需一个标签。 - A-Frame 是专为 Web 开发者设计的。它基于 DOM,因此能像其他 Web 应用一样操作 3D/VR 内容。当然,也能结合 box、d3、React 等 JavaScript 框架一起使用。 - A-Frame 让代码结构化。Three.js 代码通常是松散的,A-Frame 在 Three.js 之上构建了一个声明式的实体组件系统(entity-component-system)。另外,组件能发布并分享出去,其他开发者能以 HTML 的形式进行使用。 代码实现如下: ``` // 引入A-Frame框架 ``` 上述代码在 A-Frame 中执行的效果如图1所示。 图1  A-Frame运行结果 图1 A-Frame 运行结果 #### 使用 Three.js 开发 VR 内容 上文中我们提到另外了一种更加靠近底层同时更加灵活生产 WebVR 内容的方法,就是直接使用 WebGL+WebVR 的 API。这种方法相对于 A-Frame 的优势在于可以将VR 的支持方便地引入到我们自己的 Web3D 引擎中,同时对于底层,特别是渲染模块可以做更多优化操作从而提升 VR 运行时的性能与体验。 如果没有自己的 Web3D 引擎也没有关系,可以直接使用成熟的渲染框架,比如 Three.js 和 Babylon.js 等,这些都是比较流行且较为出色的 Web3D 端渲染引擎(框架)。接下来就以 Three.js 为例,说明如何在其上制作 WebVR 内容。 首先,对于任何渲染程序的三个要素是相似的,即是建立好 scene、renderer、camera。设置渲染器、场景以及摄像机的操作如下: ``` var renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement); // 创建Three.js的场景 var scene = new THREE.Scene(); // 创建Three.js的摄像机 var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 10000); // 调用WebVR API中的摄像机控制器对象,并将其与主摄像机进行绑定 var controls = new THREE.VRControls(camera); // 设置为站立姿态 controls.standing = true; // 调用WebVR API中的渲染控制器对象,并将其与渲染器进行绑定 var effect = new THREE.VREffect(renderer); effect.setSize(window.innerWidth, window.innerHeight); // 创建一个全局的VR管理器对象,并进行初始化的参数设置 var params = { hideButton: false, // Default: false. isUndistorted: false // Default: false. }; var manager = new WebVRManager(renderer, effect, params); ``` 上述代码即完成了渲染前的初始化设置。接下来需要向场景中加具体的模型对象,主要操作如下所示: ``` function onTextureLoaded(texture) { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.repeat.set(boxSize, boxSize); var geometry = new THREE.BoxGeometry(boxSize, boxSize, boxSize); var material = new THREE.MeshBasicMaterial({ map: texture, color: 0x01BE00, side: THREE.BackSide }); // Align the skybox to the floor (which is at y=0). skybox = new THREE.Mesh(geometry, material); skybox.position.y = boxSize/2; scene.add(skybox); // For high end VR devices like Vive and Oculus, take into account the stage // parameters provided. setupStage(); } // Create 3D objects. var geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5); var material = new THREE.MeshNormalMaterial(); var targetMesh = new THREE.Mesh(geometry, material); var light = new THREE.DirectionalLight( 0xffffff, 1.5 ); light.position.set( 10, 10, 10 ).normalize(); scene.add( light ); var ambientLight = new THREE.AmbientLight(0xffffff); scene.add(ambientLight); var loader = new THREE.ObjectLoader(); loader.load('./assets/scene.json', function (obj){ mesh = obj; // Add cube mesh to your three.js scene scene.add(mesh); mesh.traverse(function (node) { if (node instanceof THREE.Mesh) { node.geometry.computeVertexNormals(); } }); // Scale the object mesh.scale.x = 0.2; mesh.scale.y = 0.2; mesh.scale.z = 0.2; targetMesh = mesh; // Position target mesh to be right in front of you. targetMesh.position.set(0, controls.userHeight * 0.8, -1); }); ``` 最后的操作便是在 requestAnimationFrame 设置更新。在 animate 的函数中,我们要不断地获取 HMD 返回的信息以及对 camera 进行更新。 ``` // Request animation frame loop function var lastRender = 0; function animate(timestamp) { var delta = Math.min(timestamp - lastRender, 500); lastRender = timestamp; // Update VR headset position and apply to camera. //更新获取HMD的信息 controls.update(); // Render the scene through the manager. //进行camera更新和场景绘制 manager.render(scene, camera, timestamp); requestAnimationFrame(animate); } ``` 最后,程序运行的效果如图2所示,可以直接在手机上通过 VR 模式并配合 Google Cardboard 即可体验无需下载的 VR 内容[3]。 ![enter image description here](http://images.gitbook.cn/218ed470-0ae4-11e8-b617-813d2e493d45) 图2 Three.js 运行结果 上述示例中的代码实现可从[4]中下载。 ### 经验与心得 通过上述介绍我们基本可以实现一个具有初步交互体验的 Web 端 VR 应用,但这只是第一步,单纯技术上的实现距离真正的可工程化还有一定差距。因为最终工程化之后面向用户的产品必须比技术原型要考虑更多具体的东西,比如渲染的质量、交互的流畅度、虚拟化的沉浸度等,这些都最终决定用户是否会持续使用产品、接受产品所提供的服务等,所以将上述技术在工程化应用之前还有很多的优化与改进工作要做。以下是个人在做 Web 端 VR 应用过程中体会的一些心得经验,分享出来供读者参考。 - 引擎的选用。如果是使用已有的 WebGL 引擎,则可参考[5]中的文档来进行 VR SDK 集成。这里边需要做到引擎层与 VR SDK 层兼容,以及 VR 模式与引擎的工具部分的整合,也可以参考桌面引擎如 Unity3D 和 Unreal 在 VR SDK 集成上的开发模式。如果选用第三方的 WebGL 引擎则有 Three.js 或 Babylon.js 等可选,这些主流的 WebGL 引擎都已经(部分功能)集成了 VR SDK。 - 调试的设备。调试 Web 端的 VR 应用同样需要有具体的 VR 设备的支持。对于桌面 WebM 内容还是要尽量使用 HTC Vive 或 Oculus 等强沉浸感 VR 设备。对于移动 Web 应用,由于 Android 平台上的各浏览器的差异较大,表现也会不太一致,所以建议使用 iOS 设备进行开发与调试,但是在最终发布前仍要对更多的 Andnoid 设备进行适配性测试与优化。 - 性能的优化。在 Web 端做三维的绘制与渲染,性能还是主要瓶颈,因而要尽可能的提高实时渲染的性能,这样才能有更多资源留给 VR 部分。目前的 WebVR 在渲染实时中并没有像桌面 VRSDK 一样可以调用众多的 GPU 底层接口做诸如 Stereo rendering 等深层次的优化,因而对性能的占用还是较多。 - 已知的问题。目前,WebVR 仍然不太稳定,还会有诸多的 Bug,比如某些情况下会有设备跟踪丢失的情况,而且效率也不是太高。大多数 WebVR 应用可以作为后期产品的储备和预研,但要推出真正可供用户使用并流畅体验的产品,还是有较长的路要走。 ### 结束语 许多人将即将过去的2016称为 VR 元年,在这一年中 VR 的确经历了突飞猛进的发展,体现在技术与生态等各个方面。在新的2017年,相信 VR 必将会有更大的发展与进步,作为技术工作者,我们更应该从自身的技术专长作为出发点,参与到新技术对社会与生活的变革中来。 #### 参考链接 [1] http://kripken.github.io/emscripten-site/
[2] https://aframe.io/
[3] http://www.shxt3d.com/ WebVR /index.html
[4] https://github.com/bugrunnerzhang/hello WebVR.git
[5] https://mozVR.com/