AR:增强现实(Augmented Reality,简称AR):是一种实时地计算摄影机影像的位置及角度并加上相应图像、视频、3D模型的技术,这种技术的目标是在屏幕上把虚拟世界套在现实世界并进行互动。
实现分析
既然是手机AR,那么意味着有两个前提
- H5 意味着在手机而非 PC 上实现
- AR 意味着需要获取实时的视频流目前 Web 中实现 AR
都需要开启摄像头(手机具有先天优势),而获取实时视频流。然后再将虚拟物体以某种方式结合到画面中。
综合考虑,可以得出实现一个Web AR大概需要经历以下步骤:
- 获取视频源
- 将虚拟物体叠加在视频源上
- 将最终画面显示在屏幕上
- 用户与设备进行交互
技术分析
获取视频
浏览器可以用过navigator.getUserMedia()
这一API获取用户的摄像头数据流与麦克风数据流(Stream)。在使用该API时,我们发现该API已被废弃。通过MDN查询该API资料可得:
其新的标准是 接口来调用 方法,该方法通过提示用户,让用户选择是否打开系统上的相机或麦克风等,并提供包含音视频的 MediaStream。但经过尝试,仍未解决在安卓webkit内核浏览器中调用后置摄像头问题,使用一实验性方法mediaDevices.enumerateDevices(),此API仅能在支持ES6语法的手机上运行。
注:IOS不支持调用摄像头,webkit内核浏览器调用摄像头需通https。
constraints需要请求的媒体类型对象,如{audio: true, video:true}
{ audio: true, video: { facingMode: { exact: “environment” } } }
。
在实际操作过程中,我们发现即便是传入的参数强制指定为后置摄像头,在安卓手机QQ等一些 或浏览器中并不能生效。因此我们需要通过强制指定请求媒体的设备id来实现打开后置摄像头。方法如下
核心代码如下:
通过 mediaDevices.enumerateDevices()
方法来获得设备上所有的媒体输入和输出设备信息,比如摄像头,麦克风等。通过枚举这些设备,判断设备标签(device.label)中是否有 back 关键词来获取后置摄像头的设备id device.deviceId
。 device.kind == ‘videoinput’
是对 firefox 的兼容处理,因为 firefox 中的 device.deviceId
为空。
获取设备id信息后,再构造 constraints
对象使用,即通过 optional
指定 sourceId
为指定的设备id。
注意:mediaDevices.enumerateDevices()
目前还处于试验性标准阶段,实现该方法的浏览器目前还不多。对于不支持此方法的,我们直接不做调起后置摄像头的尝试,转而使用3D全景图。
其中使用了ES6所具有的方法与语法,相关方法支持度如图黑色框:
可以看出,IOS9以及安卓5.1以上的版本支持友好。对于不支持ES6的设备,调用3D全景图进行替换。
3D全景图
对于不支持调用摄像头的设备,我们输出3D全景图对其进行兼容。考虑到以后模型等使用three.js进行建立,所以3D全景我们也使用three.js来制作。通过three.js构造一个球体,将全景图片作为材质对其进行贴合。
全景图示例:
创建球形3D全景核心代码如下:
考虑到用户进入页面时候的手机角度有各种情况,可能是竖着手机进入,然后平放手机等待加载完成查看页面……并且为了降低交互难度以及固定让3D模型出现在舞台上(背景比较好看的情况下),需要的是在不同朝向(东南西北)打开3D全景,都是看向一个固定方向的。强制其朝向同一个方向。
陀螺仪绑定
无论是AR亦或是VR,其核心交互少不了根据所持设备方向变换而展示不同场景,这其中的核心便是根据陀螺仪改变场景。DeviceOrientationControls.js– 这是一个 three.js 插件,它帮助我们完成之前提到过的监视设备朝向,并且代码量非常少,通过它我们可以省去将陀螺仪alpha、beta、gamma值计算为我们所需值的步骤。
代码如下:
用户与设备交互
目前实现了两种交互方式
1. 用户通过移动手机,使手机中心点与我们设立的目标事物某一点对齐,完成交互。
实现思路是通过监听陀螺仪数据与目标事物当前的位置通过一定的计算进行对比。当到达某一临界值,判断其对齐成功。
具体代码如下:
- 用户通过点击屏幕进行交互
在页面中插入3D模型,不光只是为了看,更重要的是实现交互,比如点击,拖拽等。在 Three.js 模型中实现点击操作,可以使用 Three.js 自带的射线检测( Raycaster )。
其原理是:在用户点击时,获取点击点的屏幕坐标;根据场景中照相机,将屏幕坐标转换成场景中的坐标;新建一条从照相机位置,向转换后的场景坐标位置射出一条射线(向量),检测这条射线穿过的场景中的物体。
关键代码:
这是里涉及到Three.js里的两个坐标系,世界坐标系和屏幕坐标系。屏幕坐标系是原点基于窗口左上角的坐标系。Threejs的世界坐标系与openGL的世界坐标系相同,以屏幕中心为原点(0,0,0),并且始终不变。面对屏幕,右边为x轴的正轴,上面为y轴的正轴,屏幕向上的地方为z轴的正轴。也称为右手坐标系。
结语
新技术的预研一路上会遇到很多坑,当解决了这个问题之后又会发现出现了新问题。不过踩的坑越多,才能知道越多