如下内容转载自多多洛爱学习的文章《WebAR技术探索-导航中的应用》
做者:多多洛爱学习
连接: http://www.javashuo.com/article/p-wyiolwvp-hp.html
来源:掘金
著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。
本文探索在Web前端实现AR导航效果的前沿技术和难点。html
加强现实(Augmented Reality,简称AR):是一种实时地计算摄影机影像的位置及角度并加上相应图像、视频、3D模型的技术,这种技术的目标是在屏幕上把虚拟世界套在现实世界并进行互动。前端
通常在web中实现AR效果的主要步骤以下:html5
AR导航比较特殊的地方是,它并不是经过识别marker来肯定虚拟物体的叠加位置,而是经过定位将虚拟和现实联系在一块儿,主要步骤以下:web
坐标系转换:编程
如上文所述AR导航的主要步骤,其中难点在于:浏览器
不一样设备不一样操做系统以及不一样浏览器带来的兼容性问题主要体如今对获取视频流和获取设备陀螺仪信息的支持上。安全
navigator.getUserMedia()
已不推荐使用,目前新标准采用navigator.mediaDevices.getUserMedia()
。但是不一样浏览器对新方法的支持程度不一样,须要进行判断和处理。同时,若是采用旧方法,在不一样浏览器中方法名称也不尽相同,好比webkitGetUserMedia
。微信
//不支持mediaDevices属性 if (navigator.mediaDevices === undefined) { navigator.mediaDevices = {}; } //不支持mediaDevices.getUserMedia if (navigator.mediaDevices.getUserMedia === undefined) { navigator.mediaDevices.getUserMedia = function(constraints) { var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; if(!getUserMedia) { return Promise.reject(new Error('getUserMedia is not implemented in this browser')); } return new Promise(function(resolve, reject) { getUserMedia.call(navigator, constraints, resolve, reject); }); } }
getUserMedia
接收一个MediaStreamConstraints
类型的参数,该参数包含两个成员video
和audio
。app
var constraints = { audio: true, video: { width: { min: 1024, ideal: 1280, max: 1920 }, height: 720, frameRate: { ideal: 10, max: 15 }, facingMode: "user" // user/environment,设置先后摄像头 } }
在使用WebAR导航时,须要调取后置摄像头,然而facingMode
参数目前只有Firefox和Chrome部分支持,对于其余浏览器(微信、手Q、QQ浏览器)须要另外一个参数optional.sourceId
,传入设备媒体源的id
。经测试,该方法在不一样设备不一样版本号的微信和手Q上表现有差别。dom
if(MediaStreamTrack.getSources) { MediaStreamTrack.getSources(function (sourceInfos) { for (var i = 0; i != sourceInfos.length; ++i) { var sourceInfo = sourceInfos[i]; //这里会遍历audio,video,因此要加以区分 if (sourceInfo.kind === 'video') { exArray.push(sourceInfo.id); } } constraints = { video: { optional: [{ sourceId: exArray[1] //0为前置摄像头,1为后置 }] } }; }); } else { constraints = { video: { facingMode: { exact: 'environment' } } }); }
因为苹果的安全机制问题,iOS设备任何浏览器都不支持getUserMedia()。因此没法在iOS系统上实现WebAR导航。
出于安全考虑,Chrome47以后只支持HTTPS页面获取视频源。
设备的转动角度表明了用户的视角,也是链接虚拟和现实的重要参数。HTML5提供DeviceOrientation API
能够实时获取设备的旋转角度参数。经过监听deviceorientation
事件,返回DeviceOrientationEvent
对象。
{ absolute: [boolean] 是否为绝对转动值 alpha: [0-360] beta: [-180-180] gamma: [-90-90] }
其中alpha、beta、gamma
是咱们想要获取的角度,它们各自的意义能够参照下图和参考文章:
然而iOS系统的webkit内核浏览器中,该对象还包括webkitCompassHeading
成员,其值为设备与正北方向的偏离角度。同时iOS系统的浏览器中,alpha
并不是绝对角度,而是以开始监听事件时的角度为零点。
Android系统中,咱们可使用-alpha
获得设备与正北方的角度,可是就目前的测试状况看来,该值并不稳定。因此在测试Demo中加入了手动校订alpha
值的过程,在导航开始前将设备朝向正北方来获取绝对0度,虽然不严谨但效果还不错。
WebGL是在浏览器中实现三维效果的一套规范,AR导航须要绘制出不一样距离不一样角度的标记点,就须要三维效果以适应真实场景视频流。然而WebGL原生的接口很是复杂,Three.js是一个基于WebGL的库,它对一些原生的方法进行了简化封装,使咱们可以更方便地进行编程。
Three.js中有三个主要概念:
在AR导航的代码中,我对Three.js的建立过程进行了封装,只需传入DOM元素(通常为<div>
,做为容器)和参数,自动建立三大组件,并提供了Three.addObject
和Three.renderThree
等接口方法用于在场景中添加/删除物体或更新渲染等。
function Three(cSelector, options) { var container = document.querySelector(cSelector); // 建立场景 var scene = new THREE.Scene(); // 建立相机 var camera = new THREE.PerspectiveCamera(options.camera.fov, options.camera.aspect, options.camera.near, options.camera.far); // 建立渲染器 var renderer = new THREE.WebGLRenderer({ alpha: true }); // 设置相机转动控制器 var oriControls = new THREE.DeviceOrientationControls(camera); // 设置场景大小,并添加到页面中 renderer.setSize(container.clientWidth, container.clientHeight); renderer.setClearColor(0xFFFFFF, 0.0); container.appendChild(renderer.domElement); // 暴露在外的成员 this.main = { scene: scene, camera: camera, renderer: renderer, oriControls: oriControls, } this.objects = []; this.options = options; } Three.prototype.addObject = function(type, options) {...} // 向场景中添加物体,type支持sphere/cube/cone Three.prototype.popObject = function() {...} // 删除场景中的物体 Three.prototype.setCameraPos = function(position) {...} // 设置相机位置 Three.prototype.renderThree = function(render) {...} // 渲染更新,render为回调函数 Three.prototype.setAlphaOffset = function(offset) {..} // 设置校订alpha的偏离角度
在控制相机的转动上,我使用了DeviceOrientationControls
,它是Three.js官方提供的相机跟随设备转动的控制器,实现对deviceorientation
的侦听和对DeviceOrientationEvent
的欧拉角处理,并控制相机的转动角度。只需在渲染更新时调用一下update
方法:
three.renderThree(function(objects, main) { animate(); function animate() { window.requestAnimationFrame(animate); main.oriControls.update(); main.renderer.render(main.scene, main.camera); } });
咱们的调研中目前有三种获取定位的方案:原生navigator.geolocation
接口,腾讯前端定位组件,微信JS-SDK地理位置接口:
navigator.geolocation
接口提供了getCurrentPosition
和watchPosition
两个方法用于获取当前定位和监听位置改变。通过测试,Android系统中watchPosition
更新频率低,而iOS中更新频率高,但抖动严重。
使用前端定位组件须要引入JS模块(https://3gimg.qq.com/lightmap/components/geolocation/geolocation.min.js
),经过 qq.maps.Geolocation(key, referer)
构造对象,也提供getLocation
和watchPosition
两个方法。通过测试,在X5内核的浏览器(包括微信、手Q)中,定位组件比原生接口定位更加准确,更新频率较高。
微信JS-SDK地理位置接口
使用微信JS-SDK接口,咱们能够调用室内定位达到更高的精度,可是须要绑定公众号,只能在微信中使用,仅提供getLocation
方法,暂时不考虑。
综上所述,咱们主要考虑在X5内核浏览器中的实现,因此选用腾讯前端定位组件获取定位。可是在测试中仍然暴露出了定位不许确的问题:
针对该问题,我设计了优化轨迹的方法,进行定位去噪、肯定初始中心点、根据路径吸附等操做,以实现移动时的变化效果更加平稳且准确。
咱们经过getLocation
和watchPosition
方法获取到的定位数据包含以下信息:
{ accuracy: 65, lat: 39.98333, lng: 116.30133 ... }
其中accuracy
表示定位精度,该值越低表示定位越精确。假设定位精度在固定的设备上服从正态分布(准确来讲应该是正偏态分布),统计整条轨迹点定位精度的均值mean
和标准差stdev
,将轨迹中定位精度大于mean + (1~2) * stdev
的点过滤掉。或者采用箱型图的方法去除噪声点。
初始点很是重要,若初始点偏离,则路线不许确、虚拟现实没法重叠、没法获取到正确的移动路线。测试中我发现定位开始时得到的定位点大多不太准确,因此须要一段时间来肯定初始点。
定位开始,设置N
秒用以获取初始定位。N
秒钟获取到的定位去噪以后造成一个序列track_denoise = [ loc0, loc1, loc2...]
,对该序列中的每个点计算其到其余点的距离之和,并加上自身的定位精度,获得一个中心衡量值,而后取衡量值最小的点为起始点。
基于设备始终跟随规划路线进行移动的假设,能够将定位点吸附到规划路线上以防止3D图像的抖动。
以下图所示,以定位点到线段的映射点做为校订点。路线线段的选择依据以下:
cur = 0; P_cur = P[cur];
N
条线段上移动时,若映射长度(映射点与线段起点的距离)为负,校订点取当前线段的起点,线路回退至上一线段,cur = N - 1; P_cur = P[cur];
;若映射长度大于线段长度,则校订点取当前线段的终点,线路前进至下一线段,cur = N + 1; P_cur = P[cur];
WebGL中的单位长度与现实世界的单位长度并无肯定的映射关系,暂时还没法准确进行映射。经过测试,暂且选择1(米):15(WebGL单位长度)。
演示视频:WebAR技术探索-导航中的应用
对地图感兴趣的开发者,可登陆腾讯位置服务体验~