Web前端AR技術探索-導航中的應用

本文探索在Web前端實現AR導航效果的前沿技術和難點。html

1. AR簡介

加強現實(Augmented Reality,簡稱AR):是一種實時地計算攝影機影像的位置及角度並加上相應圖像、視頻、3D模型的技術,這種技術的目標是在屏幕上把虛擬世界套在現實世界並進行互動。前端

通常在web中實現AR效果的主要步驟以下:html5

  1. 獲取視頻源
  2. 識別marker
  3. 疊加虛擬物體
  4. 顯示最終畫面

以上參考:如何經過 Web 技術實現一個簡單但有趣的 AR 效果web

AR導航比較特殊的地方是,它並不是經過識別marker來肯定虛擬物體的疊加位置,而是經過定位將虛擬和現實聯繫在一塊兒,主要步驟以下:編程

  1. 獲取視頻源
  2. 座標系轉換:
    1. 獲取設備和路徑的絕對定位
    2. 計算路徑中各標記點與設備間的相對定位
    3. 在設備座標系中繪製標記點
  3. 3D圖像與視頻疊加
  4. 更新定位和設備方向,控制Three.js中的相機移動

2. 技術難點

如上文所述AR導航的主要步驟,其中難點在於:瀏覽器

  1. 兼容性問題
  2. WebGL三維做圖
  3. 定位的精確度和軌跡優化
  4. 虛擬和現實單位尺度的映射

2.1 兼容性問題:

不一樣設備不一樣操做系統以及不一樣瀏覽器帶來的兼容性問題主要體如今對獲取視頻流和獲取設備陀螺儀信息的支持上。安全

2.1.1 獲取視頻流

  1. Navigator API兼容處理bash

    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);
    		});
    	}
    }
    複製代碼
  2. 參數兼容處理app

    getUserMedia接收一個MediaStreamConstraints類型的參數,該參數包含兩個成員videoaudio

    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上表現有差別。

    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'
    			}
    		}
    	});
    }
    複製代碼
  3. 操做系統的兼容性問題

    因爲蘋果的安全機制問題,iOS設備任何瀏覽器都不支持getUserMedia()。因此沒法在iOS系統上實現WebAR導航。

  4. 協議

    出於安全考慮,Chrome47以後只支持HTTPS頁面獲取視頻源。

2.1.2 獲取設備轉動角度

設備的轉動角度表明了用戶的視角,也是鏈接虛擬和現實的重要參數。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度,雖然不嚴謹但效果還不錯。

手動校訂alpha

2.2 WebGL三維做圖

WebGL是在瀏覽器中實現三維效果的一套規範,AR導航須要繪製出不一樣距離不一樣角度的標記點,就須要三維效果以適應真實場景視頻流。然而WebGL原生的接口很是複雜,Three.js是一個基於WebGL的庫,它對一些原生的方法進行了簡化封裝,使咱們可以更方便地進行編程。

Three.js中有三個主要概念:

  1. 場景(scene):物體的容器,咱們要繪製標記點就是在場景中添加指定座標和大小的球體
  2. 相機(camera):模擬人的眼睛,決定了呈現哪一個角度哪一個部分的場景,在AR導航中,咱們主要經過相機的移動和轉動來模擬設備的移動和轉動
  3. 渲染器(renderer):設置畫布,將相機拍攝的場景呈如今web頁面上

在AR導航的代碼中,我對Three.js的建立過程進行了封裝,只需傳入DOM元素(通常爲<div>,做爲容器)和參數,自動建立三大組件,並提供了Three.addObjectThree.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);
    }
});
複製代碼

2.3 定位的精確度和軌跡優化

咱們的調研中目前有三種獲取定位的方案:原生navigator.geolocation接口,騰訊前端定位組件,微信JS-SDK地理位置接口:

  1. 原生接口

    navigator.geolocation接口提供了getCurrentPositionwatchPosition兩個方法用於獲取當前定位和監聽位置改變。通過測試,Android系統中watchPosition更新頻率低,而iOS中更新頻率高,但抖動嚴重。

  2. 前端定位組件

    使用前端定位組件須要引入JS模塊(https://3gimg.qq.com/lightmap/components/geolocation/geolocation.min.js),經過 qq.maps.Geolocation(key, referer)構造對象,也提供getLocationwatchPosition兩個方法。通過測試,在X5內核的瀏覽器(包括微信、手Q)中,定位組件比原生接口定位更加準確,更新頻率較高。

  3. 微信JS-SDK地理位置接口

    使用微信JS-SDK接口,咱們能夠調用室內定位達到更高的精度,可是須要綁定公衆號,只能在微信中使用,僅提供getLocation方法,暫時不考慮。

    綜上所述,咱們主要考慮在X5內核瀏覽器中的實現,因此選用騰訊前端定位組件獲取定位。可是在測試中仍然暴露出了定位不許確的問題:

    1. 定位不許致使虛擬物體與現實沒法準確疊加
    2. 定位的抖動致使虛擬標記點跟隨抖動,移動視覺效果不夠平穩

針對該問題,我設計了優化軌跡的方法,進行定位去噪、肯定初始中心點、根據路徑吸附等操做,以實現移動時的變化效果更加平穩且準確。

2.3.1 定位去噪

咱們經過getLocationwatchPosition方法獲取到的定位數據包含以下信息:

{
	accuracy: 65,
	lat: 39.98333,
	lng: 116.30133
	...
}
複製代碼

其中accuracy表示定位精度,該值越低表示定位越精確。假設定位精度在固定的設備上服從正態分佈(準確來講應該是正偏態分佈),統計整條軌跡點定位精度的均值mean和標準差stdev,將軌跡中定位精度大於mean + (1~2) * stdev的點過濾掉。或者採用箱型圖的方法去除噪聲點。

2.3.2 初始點肯定

初始點很是重要,若初始點偏離,則路線不許確、虛擬現實沒法重疊、沒法獲取到正確的移動路線。測試中我發現定位開始時得到的定位點大多不太準確,因此須要一段時間來肯定初始點。

定位開始,設置N秒用以獲取初始定位。N秒鐘獲取到的定位去噪以後造成一個序列track_denoise = [ loc0, loc1, loc2...],對該序列中的每個點計算其到其餘點的距離之和,並加上自身的定位精度,獲得一箇中心衡量值,而後取衡量值最小的點爲起始點。

初始點糾偏

2.3.3 基於路線的定位校訂

基於設備始終跟隨規劃路線進行移動的假設,能夠將定位點吸附到規劃路線上以防止3D圖像的抖動。

以下圖所示,以定位點到線段的映射點做爲校訂點。路線線段的選擇依據以下:

  1. 初始狀態:以起始點與第二路線點之間的線段爲當前線段,cur = 0; P_cur = P[cur];
  2. 在第N條線段上移動時,若映射長度(映射點與線段起點的距離)爲負,校訂點取當前線段的起點,線路回退至上一線段,cur = N - 1; P_cur = P[cur];;若映射長度大於線段長度,則校訂點取當前線段的終點,線路前進至下一線段,cur = N + 1; P_cur = P[cur];
  3. 若當前線段與下一線段的有效範圍有重疊區域(以下圖綠色陰影區),則需判判定位點到兩條線段的距離,以較短的爲準,肯定校訂點和線路選擇。

基於路線的定位糾偏

2.4 虛擬和現實的單位長度映射

WebGL中的單位長度與現實世界的單位長度並無肯定的映射關係,暫時還沒法準確進行映射。經過測試,暫且選擇1(米):15(WebGL單位長度)。

3. demo演示

演示視頻:WebAR技術探索-導航中的應用

相關文章
相關標籤/搜索