![](http://bugly.qq.com/bbs/data/attachment/forum/201605/26/105823n5a9lwxv9nkvv05a.jpg)
近来风生水起的 VR 虚拟现实技术,抽空想起年初完成的“星球计划”项目,总结篇文章与各位分享一下制作基于 Html5 的 3D 全景漫游秘籍。
————本文很长——能看完是真爱————
【 关于”星球计划” 】
QQ 物联与深圳市天文台合作,在手Q “发现新设备”-“公共设备”里,连接QQ 物联摄像头为用户提供2016年天体大事件的直播,大家可以通过手Q 实时观看到世界各地最佳观测点的日食,流星等天体现象。承载整个“星球计划”活动的运营页面,经多方讨论,我们决定尝试 3D 全景漫游模式的 H5 运营页进行推广。
![](http://i.imgur.com/capaK8z.png)
先贴一个体验地址(请忽略 GIF 录屏的卡顿及字体,iOS 开启陀螺仪体验最佳),Page3 的宇宙部分-转动手机在模拟的宇宙里搜寻各大行星,就是我们今天要说的基于 Html5 的 3D 全景漫游。
![](http://bugly.qq.com/bbs/data/attachment/forum/201605/26/110052ruz4f7leek8jc8za.gif)
![](http://bugly.qq.com/bbs/data/attachment/forum/201605/26/110102qkxkq9wwwzwxgx35.jpg)
要制作全景漫游,首先得有全景图像。全景图像的获取通常是借助鱼眼的全景摄像机的拍摄来完成的,或者是单反相机、鱼眼镜头、云台和三角架的组合。需要按照一个方向旋转360度拍摄一组照片,照片之间要有部分重合的部分,方便进行后期的拼接和融合。在拍好照片后需要将它们无缝拼接在一起,生成的全景图像可分为球面全景图、立方体全景图以及柱状全景图等。(咋们腾讯地图的街景体验,就是最常见的全景漫游技术啦)
没有全景摄像机,也可以通过一些素材站点拿到适合我们项目的全景图。
![](http://i.imgur.com/5vAOSQe.png)
当然,星球计划的背景图是宇宙星际,相对而言是无序的,所以靠视觉设计师进行拼接绘制也是可以的。
而什么是全景漫游呢,全景漫游技术可以让体验者在全景图像构建的全景空间里切换视角的浏览。它是通过拍摄全景图像,再采用计算机图形图像技术构建出全景空间,让使用者能用控制浏览的方向,或左或右、或上或下观看物体或场景,仿佛身临其境一般。与传统的 3D 建模相比,全景漫游技术制作简单,数据量小,系统消耗低,且更有真实感。故近年来,也是 VR 技术的一大热门实现手法,用前面的贴图例子来个 demo。而在移动端的全景漫游,更是可以绑定陀螺仪,让你更有身临其境的感觉。
![](http://i.imgur.com/eQxv6p0.gif)
在项目初期,预研了一些全景漫游制作方案,包括目前最为常用的全景漫游制作工具是 Pano2vr & Krpano,以及用 Flash,QuickTime,基于 Java,js 等其他方式制作全景漫游,但据预研所了解的个方案优劣势对比图如下图,结合“星球计划”项目的具体情况,最终优先选择了Threejs的实现方案。
![](http://i.imgur.com/daUwOHj.jpg)
这里顺便和大家聊聊目前最为常用的全景漫游制作工具是 Pano2vr & Krpano。
(1)Pano2vr操作简单功能虽少但非常实用,“导入全景图-设置交互热点-微调-导出”即可直接生产 flash,html5,Quicktime 等格式。
Pano2vr 对于仅用在 PC,iOS 上的需求非常合适快捷,但对 Android 机的支持表现不佳。
![](http://i.imgur.com/5ppT7mf.jpg)
(2)Krpano,功能强大完善,各平台兼容性高,拓展性很强,各类VR场景特效都可承载。但自成体系,需要遵循 Krpano xml 的这套编程语言,没有 gui 的软件界面,新手上手及后期维护成本较大,生成的全景漫游专业但对载入速度,内存占用都有影响。但对于想做高阶的,个性化,定制化全景漫游项目,Krpano 是不二选择。
但这2个工具都需要购买授权码才可商用,非免费。
![](http://i.imgur.com/XItGSmM.png)
(3)Three.js 源自 Github 的一个开源项目,[url=https://github.com/mrdoob/three.js,官网:http://threejs.org/]https://github.com/mrdoob/three.js,官网:http://threejs.org/[/url] 。
看到一个同行的解释,说理解成 Three + js 就可以了。Three 表示 3D 的意思,js 表示 javascript 的意思,故 three.js 就是使用 javascript 来写 3D 程序的意思,格外的直白清晰啊。依靠 javascript 的语言编写,自然给这个方案带来了高拓展,高兼容,低开发成本,可高性能且免费的几大优势。
(4)Flash,QuickTime,基于 Java,js 等其他方式这里就不一一详述,大致的优劣势对比请参考上面的表格(具体评分仅供参考,软件版本更新也许会有各方面的升级)。
想要利用 Three.js 制作一个物体渲染到网页中去,需要构建这3个组建:场景(scene)、相机(camera)和渲染器(renderer):
(1)场景(scene)
即是画布,是所有物体 object 的容器。在最开始的时候对场景实例化,将之后构建的物体都添加到场景中即可。
![](http://i.imgur.com/DUS0OFr.png)
用户是通过相机 Camare 来查看在 scene 下的 3d 场景,在 three.js 里包含了正交投影照相机(Orthographic Camera)和透视投影照相机(Perspective Camera)2种,从模拟人眼看物体的方式来选,透视投影照相机更适合。如下图所示,fov 是相机视角的夹角,aspect 等于相机画幅比例,near 和 far 分别是照相机到视景体最近、最远的距离,均为正值,且 far 应大于 near。
![](http://bugly.qq.com/bbs/data/attachment/forum/201605/26/110122fclkzlw1kywyeaz1.jpg)
![](http://bugly.qq.com/bbs/data/attachment/forum/201605/26/110133l1ww997kwwhkevj3.jpg)
渲染器是用来设定渲染的结果会在页面的什么元素上面呈现,以及按什么规则来渲染。
![](http://i.imgur.com/qMzEuNp.png)
在 Three.js 中,场景是容器,把我们星球计划的星星们放置在构建的3D场景中的不同位置;相机对着下场景拍摄,拍摄结果通过渲染器实时的绘制在我们的浏览器上。
![](http://i.imgur.com/02ehPDI.jpg)
定义了这三大元素之后,下一步,就是构建我们的星球计划所需的宇宙了。前文有提到,全景图像可分为球面全景图、立方体全景图以及柱状全景图。
虽然球面全景图具有和人眼最接近的构建模式,但需要很多个立面才可以构建成一个球体,球面的经纬度坐标无法展开成一个平面贴图,相对于其他方案,性能消耗过高,拼接方法过于繁琐;而柱形全景图的垂直视野小,不好做顶部底部的俯仰视角。我们选定了最常见的立方图全景图来构建我们的3D场景。
立方体全景图有6个面,我们需要定义每个面贴图的背景图片,3D位置,旋转角度(默认的6个面都是朝着我们的,我们需要定义朝坐标轴的各个方向做90度的旋转,才可以搭建成一个立方体)。
![](http://bugly.qq.com/bbs/data/attachment/forum/201605/26/110306hiz0pdptiqn2q29i.gif)
![](http://bugly.qq.com/bbs/data/attachment/forum/201605/26/110319o5iq5uzonq3zkbki.jpg)
![](http://bugly.qq.com/bbs/data/attachment/forum/201605/26/110332jz8upipzt6ji4ju6.jpg)
然后添加到 THREE.Object3D 的数组中,这样我们就在场景中构建好了一个 3D 的宇宙空间。
![](http://i.imgur.com/s98u2yw.png)
这里,考虑到星空背景主要为了氛围烘托,我们将6个面的贴图减少成2个,以此缩减了资源文件的大小,如下图所示。
这里我们用的是 Threejs 的 实时渲染:就是需要不停的对画面进行渲染,即使画面中什么也没有改变,也需要重新渲染。其中一个重要的函数是 requestAnimationFrame,这个函数就是让浏览器去执行一次参数中的函数,这样通过上面 animate 中调用 requestAnimationFrame() 函数,requestAnimationFrame()函数又让 animate()再执行一次,就形成了我们通常所说的渲染循环 了。
![](http://i.imgur.com/lhFrmRs.png)
通过上面这些步骤,我们就构建好这个3D的宇宙空间了。
![](http://i.imgur.com/svhgBIz.gif)
一期的星球计划中,需要增加8颗星球,为了避免画面过于拥挤,星球们被分散定位在了6个面上。之前我们定义宇宙六个面的时候给了每个面一个固定的 section id,通过简单的 js 我们可以往平面中加入星球的 DOM 结构。
![](http://i.imgur.com/qbolpCq.jpg)
因此也可以很轻松的利用 CSS 给这些星球定位及增加个性的动画效果,这里要特别注意,不要增加阴影等耗内存特别大的动画效果,它们是 Crash 罪魁祸首。
![](http://bugly.qq.com/bbs/data/attachment/forum/201605/26/110348bb68f8cbvot5c52b.gif)
![](http://bugly.qq.com/bbs/data/attachment/forum/201605/26/110359g112d19284a4id8z.jpg)
最后一步,将全景漫游绑定陀螺仪,这里涉及到需要对陀螺仪事件做个保护代码,判断机器是否支持陀螺仪。完成以上几步,既可以实现一个在移动端的全景漫游啦。
![](http://i.imgur.com/LzoDxxK.gif)
(8)其他
在项目完成的初期,对部分安卓机的内存消耗还是过大,为此在完成项目之后继续尝试了一些优化工作,包括 缩减宇宙的尺寸,合并全景贴图,禁用陀螺仪,预加载和懒加载,星球 CSS3 动画缩减,资源文件深度压缩等工作,但还是无法避免在内存不足的安卓机下存在 Crash 的风险,为保证项目的稳定上线,退而求其次对安卓机做了兼容版的体验,预期在后续的项目迭代中再优化页面在安卓下的表现,实现全平台的体验统一。
最后,仅以此文总结在移动端构建 3D 全景漫游的试水总结,该尝试基本上能够满足项目的需求,但在性能优化,细节完善上还继续打磨,希望能对有兴趣的小伙伴带来一些帮助^^。