使用JSARToolKit5 開發AR應用 (1) 簡介

Tony_Stark發表於2017-09-29

相關站點

jsartoolkit5 - ARToolkit.js

Talkingdata - 用資料的心智去超越

three.js 系列教程 - 良心之作

使用JSARToolKit5 開發AR應用 (1) 簡介

介紹

本文是使用JSARToolKit5構建擴增實境(AR)Web應用程式的簡短介紹。
我們將要學習什麼是JSARToolKit5,您可以使用它AR應用程式,以及如何使用Three.js 3D引擎來構建3D AR物件。
我也會簡要介紹一下AR是什麼。

擴增實境

擴增實境是一個用於談論將虛擬物件新增到現實世界中的術語。
你也可能聽說過虛擬現實,這是關於將你運送到完全虛擬的地方。
兩者使用了很多相同的技術,但用例不同。
使用AR,您可以將虛擬宜家傢俱放在您所在地的地板上,看看它們的外觀與其他傢俱相似。
使用VR,您可以將自己傳送到公寓的建築藍圖中,看看建築物的外觀。
AR將東西新增到現實世界中,VR替代現實世界。


AR的另一個用途是將智慧新增到現實世界中。
燈光開關有一條線路,他們控制的光線,樹葉告訴你什麼樣的樹,名稱懸停在人的頭上,導航箭頭出現在街上,天空中的線條指導你認識你的朋友,翻譯顯示在您正在檢視的文字之上。
向世界增加知識和遙控器。
通過了解您正在檢視的內容,您可以下拉所瞭解的內容以及您可以使用的內容。

簡而言之,JSARToolKit5是最新和最新版本的ARToolKit擴增實境庫的JavaScript埠。
您可以使用JSARToolKit5將物件新增到影像和視訊中,並使它們看起來像是現實世界場景的一部分。
不僅如此,您還可以使虛擬物件互動和動畫化。

為了演示您可以使用JSARToolKit5進行的應用程式種類,我們將編寫一個小型的AR應用程式,從裝置的相機中增加視訊。
該應用程式將在視訊Feed中放置一個小框,您可以點選框開啟它,並檢視內部的內容。

讓我們開始吧!

JSARToolKit的基礎知識

通過跟蹤視訊中的特殊影像,JSARToolKit可以起作用。
這些特殊影像稱為AR標記,JSARToolKit可以確定視訊中的哪些位置以及它們指向的方向。
通過從JSARToolKit獲取AR標記的位置和方向,您可以在正確的位置在視訊頂部繪製3D物件,使其看起來像物件是視訊的一部分。

要載入JSARToolKit,請將最小指令碼包含在您的網頁中。

<script src="build/artoolkit.min.js"></script>複製程式碼

是的,如你所想,我們將需要三件事來構建我們的AR應用程式。
我們將需要一個AR標記,一個視訊,以及在視訊之上繪製3D圖形的方法。
對於標記,我們將使用一個特殊的內建標記,稱為BarcodeMarker。
我們將使用getUserMedia API從您的裝置攝像頭獲取的視訊。
我們將使用Three.js來做3D圖形。
我們首先要做的是建立視訊源。
為此,我們將使用getUserMedia API獲取裝置攝像頭的URL,然後使用該URL作為視訊元素的源。
有了這個就可以讓視訊元素來顯示裝置攝像頭的視訊資訊。

所有這一切的簡單方法是在JSARToolKit5中使用一個幫助函式:
選項物件中的onSuccess回撥使用即用型視訊元素進行呼叫。
請注意,在Android版Chrome上,只有在與網頁進行互動後,才能播放手機影片。
ARController.getUserMedia新增了一個播放視訊的視窗級觸控事件監聽器。

ARController.getUserMedia(options)

var video = ARController.getUserMedia({
	maxARVideoSize: 320, // do AR processing on scaled down video of this size
	facing: "environment",
	onSuccess: function(video) {
		console.log('got video', video);
	}
});複製程式碼

我們需要做的第一件事就是建立一個ARController。
ARController跟蹤我們註冊的標記,並從視訊源中讀出畫素。

...

var arController = new ARController(video, 'Data/camera_para.dat');
arController.onload = function() {
	console.log('ARController ready for use', arController);
};

...複製程式碼

您也可以通過顯式載入ARCameraParam來編寫(也許您想要重用它):

...

var camera = new ARCameraParam('Data/camera_para.dat');
camera.onload = function() {
	var arController = new ARController(video.videoWidth, video.videoHeight, camera);
	console.log('ARController ready for use', arController);
};

...複製程式碼

從JSARToolKit獲取標記位置

AR標記

標記是JSARToolKit用於跟蹤視訊和影像中物件的3D位置的特殊影像。
您可以將它們視為黑客,使軟體更容易瞭解影像中的內容。
而不是必須瞭解整個影像,軟體可以專注於在影像中找到矩形形狀,並將矩形的內容與註冊的標記進行比較。

現在我們將視訊連線,我們已經準備好開始跟蹤視訊中的AR標記。
一旦我們看到一個AR標記,我們想得到它的細節,找出它在哪裡和它指向哪一個方向。

幸運的是,這個ARToolKit負責所有的計算,並給我們一個很好的易於使用的轉換矩陣。
獲取視訊中的標記。

// Set the ARController pattern detection mode to detect barcode markers.
arController.setPatternDetectionMode( artoolkit.AR_MATRIX_CODE_DETECTION );

// Add an event listener to listen to getMarker events on the ARController.
// Whenever ARController#process detects a marker, it fires a getMarker event
// with the marker details.
//
var detectedBarcodeMarkers = {};
arController.addEventListener('getMarker', function(ev) {
	var barcodeId = ev.data.marker.idMatrix;
	if (barcodeId !== -1) {
		console.log("saw a barcode marker with id", barcodeId);

		// Note that you need to copy the values of the transformation matrix,
		// as the event transformation matrix is reused for each marker event
		// sent by an ARController.
		//
		var transform = ev.data.matrix;
		if (!detectedBarcodeMarkers[barcodeId]) {
			detectedBarcodeMarkers[barcodeId] = {
				visible: true,
				matrix: new Float32Array(16)
			}
		}
		detectedBarcodeMarkers[barcodeId].visible = true;
		detectedBarcodeMarkers[barcodeId].matrix.set(transform);

	}
});

var cameraMatrix = arController.getCameraMatrix();

for (var i in detectedBarcodeMarkers) {
	detectedBarcodeMarkers.visible = false;
}

// Process a video frame to find markers in it.
// Each detected marker fires a getMarker event.
//
arController.process(video);複製程式碼

使用JSARToolKit與Three.js

現在我們有從JSARToolKit的標記位置,我們可以將它們複製應該出現在的標記的Three.js物件之上。
請注意,如果將標記移動到相機外部,則跟蹤將丟失,並且Three.js物件將不會被更新。
你可以使物件保持原來的位置,突然消失或淡出。
我喜歡淡化已經失去跟蹤的物件,但它可以有點輕而易舉地控制物件中材料的所有不透明度。

很酷的是,跟蹤標記的Three.js物件是一個普通的Three.js物件。
所以我們可以使用我們所有的通常的技巧。
對於此演示,我們將新增一個觸控事件處理程式來檢測何時觸控物件並在執行時對物件進行動畫處理。
下面的程式碼是通常的Three.js滑鼠射線播放程式碼,它將一個從滑鼠座標的射線投射到3D場景,並檢查射線對場景中的物體,看看它們中有任何一個與射線相交。
如果我們找到一個交叉點,則意味著相交物件位於滑鼠游標下方。
在你的tap事件偵聽器和presto中新增raycasting程式碼!
你有一個可以開啟的Three.js場景。

我新增了一個小動畫到tap偵聽器,所以當你點選框物件,它開啟,東西飛出來。
要檢視它,請訪問演示頁面,並允許訪問您的相機。
現在把你的相機放在一張帶標記的一張紙上。
Hohoo,你現在可以看到這個盒子。
點選它開啟它!

在程式碼方面 JSARToolKit5中使用Three.js整合。
它完成了我們上面設定的所有東西,並將其打包成幾個易於使用的方法。
只需載入

js/artoolkit.three.js

<script src="build/artoolkit.min.js"></script>
<script src="js/artoolkit.three.js"></script>
<script>
ARController.getUserMediaThreeScene(
	facing: 'environment',
	onSuccess: function(arScene, arController, arCameraParam) {

		arController.setPatternDetectionMode(artoolkit.AR_MATRIX_CODE_DETECTION);

		// Track the barcode marker with id 20.
		// markerRoot is a THREE.Object3D that tracks the marker position.
		//
		var markerRoot = arController.createThreeBarcodeMarker(20);

		// Add the openable box to the marker root.
		//
		var box = createOpenableBox();
		markerRoot.add(box);

		// Add the marker root to the AR scene.
		//
		arScene.scene.add(markerRoot);


		// Add event handlers to make the box open/close on tap.
		//
		window.addEventListener('touchend', function(ev) {
			if (box.hit( ev.touches[0], arScene.camera )) {
				box.toggleOpen();
			}
		}, false);

		window.addEventListener('mouseup', function(ev) {
			if (box.hit( ev, arScene.camera )) {
				box.toggleOpen();
			}
		}, false);


		// Create renderer and deal with mobile orientation.
		//
		var renderer = new THREE.WebGLRenderer({antialias: true});
		var f = Math.min(
			window.innerWidth / arScene.video.videoWidth,
			window.innerHeight / arScene.video.videoHeight
		);
		var w = f * arScene.video.videoWidth;
		var h = f * arScene.video.videoHeight;

		if (arController.orientation === 'portrait') {
			renderer.setSize(h,w);
			renderer.domElement.style.transformOrigin = '0 0';
			renderer.domElement.style.transform = 'rotate(-90deg) translateX(-100%)';
		} else {
			renderer.setSize(w,h);
		}
		document.body.appendChild(renderer.domElement);


		// Call arScene.renderOn on each frame,
		// it does marker detection, updates the Three.js scene and draws a new frame.
		//
		var tick = function() {
			requestAnimationFrame(tick);
			arScene.renderOn(renderer);
		};
		tick();

	}
);
</script>複製程式碼

在Firefox Android或Chrome Android上最佳瀏覽。
也可以在桌面瀏覽器上工作,儘管與網路攝像頭視訊進行互動有點麻煩。
不幸的是,iOS不支援網站上的實時攝像機,因此您無法在iOS上觀看此演示。

(ios11 目前支援)

沒有助手的JSARToolKit和Three.js

好的,所以這樣你可以和幫助者一起做一個Three.js的場景。
也許你不想因為某些原因使用助手。
或者您要將JSARToolKit與其他繪相簿一起使用。
很好,讓我來看看你如何使用原始的JSARToolKit與Three.js。

手動執行Three.js整合並不是那麼多工作,只需要一堆樣板。
首先,我們需要使用getMarker事件偵聽器跟蹤標記。
以下,我將跟蹤ID 20的條形碼標記的位置和可見性。

// Create a marker root object to keep track of the marker.
//
var markerRoot = new THREE.Object3D();

// Make the marker root matrix manually managed.
//
markerRoot.matrixAutoUpdate = false;

// Add a getMarker event listener that keeps track of barcode marker with id 20.
// 
arController.addEventListener('getMarker', function(ev) {
	if (ev.data.marker.idMatrix === 20) {

		// The marker was found in this video frame, make it visible.
		markerRoot.visible = true;

		// Copy the marker transformation matrix to the markerRoot matrix.
		markerRoot.matrix.elements.set(ev.matrix);

	}
});

// Add a cube to the marker root.
//
markerRoot.add( new THREE.Mesh(new THREE.BoxGeometry(1,1,1), new THREE.NormalGeometry()) );

// Create renderer with a size that matches the video.
//
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(video.videoWidth, video.videoHeight);
document.body.appendChild(renderer.domElement);

// Set up the scene and camera.
//
var scene = new THREE.Scene();
var camera = new THREE.Camera();
scene.add(camera);
scene.add(markerRoot);

// Make the camera matrix manually managed.
//
camera.matrixAutoUpdate = false;

// Set the camera matrix to the AR camera matrix.
//
camera.matrix.elements.set(arController.getCameraMatrix());

// On each frame, detect markers, update their positions and
// render the frame on the renderer.
//
var tick = function() {
	requestAnimationFrame(tick);

	// Hide the marker, we don't know if it's visible in this frame.
	markerRoot.visible = false;

	// Process detects markers in the video frame and sends
	// getMarker events to the event listeners.
	arController.process(video);

	// Render the updated scene.
	renderer.render(scene, camera);
};
tick();複製程式碼

不要讓上面的嚇到你,它只是得到了很多評論的幾行程式碼。
這意味著渲染渲染器上的AR場景並跟蹤條形碼標記。
您可能還想將AR場景疊加在視訊Feed的頂部。
我們補充一點。
沒有什麼太複雜,只是在其他任何事情之前用視訊繪製飛機。

// To display the video, first create a texture from it.
var videoTex = new THREE.Texture(video);

// Use linear downscaling for videoTex
// (otherwise it needs to be power-of-two sized and you
// need to generate mipmaps, which are kinda useless here)
videoTex.minFilter = THREE.LinearFilter;

// And unflip the video Y-axis.
videoTex.flipY = false;

// Then create a plane textured with the video.
var plane = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(2, 2),
  new THREE.MeshBasicMaterial({map: videoTex, side: THREE.DoubleSide})
);

// The video plane shouldn't care about the z-buffer.
plane.material.depthTest = false;
plane.material.depthWrite = false;

// Create a scene and a camera to draw the video.
var videoScene = new THREE.Scene();
var videoCamera = new THREE.OrthographicCamera(-1, 1, -1, 1, -1, 1);
videoScene.add(videoCamera);
videoScene.add(plane);

// Set the renderer autoClear to false, otherwise it
// clears the canvas before each render call.
renderer.autoClear = false;

// Draw the videoScene before the AR scene.
var tick = function() {
	requestAnimationFrame(tick);

	markerRoot.visible = false;
	arController.process(video);

	// Clear the renderer before drawing the videoScene,
	// followed by the AR scene.
	renderer.clear();
	renderer.render(videoScene, videoCamera);
	renderer.render(scene, camera);
};
tick();複製程式碼


JSARController ARController具有內建的肖像模式校正功能。
所以,如果你通過一個肖像模式的視訊,它旋轉90度,使景觀照相機引數正常工作。
其結果是,您的渲染器畫布最終在其側面,這不是我想要的。
以下是如何糾正它:

// Rotate the video plane and the renderer if the arController is in portrait mode.
if (arController.orientation === 'portrait') {
	plane.rotation.z = Math.PI/2;

	renderer.setSize(video.videoHeight, video.videoWidth);
	renderer.domElement.style.transformOrigin = '0 0';
	renderer.domElement.style.transform = 'rotate(-90deg) translateX(-100%)';
} else {
	renderer.setSize(video.videoWidth, video.videoHeight);	
}複製程式碼

如何編寫ARToolKit 風格的程式碼

如果你覺得這個整個事件迴圈不是你的風格。
JSARToolKit5還暴露了C風格的ARToolKit API。
因為這很符合大家的思維習慣!

以下是ARtoolkit使用C風格處理Marker的迴圈方法:

// Detect barcode markers
arController.setPatternDetectionMode(artoolkit.AR_MATRIX_CODE_DETECTION);

// Process the video frame.
arController.detectMarker(video);

var markerMatrix = new Float32Array(12);
var glMatrix = new Float32Array(16);

// Get the number of markers from the ARController and iterate over each marker.
var markers = arController.getMarkerNum();
for (var i=0; i < markers.length; i++) {

	var marker = arController.getMarker(i);

	// If we found the number 5 marker, let's get its transform matrix.
	if (marker.idMatrix === 5) {
		arController.getTransMatSquare(i, 1 /* marker width */, markerMatrix);

		// And convert it to a WebGL matrix.
		arController.transMatToGLMat(markerMatrix, glMatrix);

		// Do something with the glMatrix ...

	}
}複製程式碼

如果我們在前一幀中看到了標記,我們可以使用先前的標記矩陣作為矩陣計算的輸入。
通過使用先前的矩陣作為當前矩陣計算的基礎,我們可以實現更穩定的跟蹤。

// ... skip to the marker detection loop
if (marker.idMatrix === 5) {
	if (markerWasInPreviousFrame) {
		// Get the marker matrix using continuous tracking
		arController.getTransMatSquareCont(i, 1, markerMatrix, markerMatrix);
	} else {
		arController.getTransMatSquare(i, 1, markerMatrix);	
	}

	// ... The rest is as usual
}
複製程式碼

Emscripten?如何除錯?

哦,是的,ARToolKit庫的JSARToolKit5埠由Emscripten提供支援,Emscripten是令人驚奇的C / C ++到ASM.js編譯器。
因此,JSARToolKit5在Firefox附近以原始速度執行,而在其他瀏覽器上執行速度也不算太高。
而且,更好的是,從上游庫移植新功能和改進只是一個編譯(您需要新增幾行繫結。)

因此,JSARToolKit帶有兩種變體。
一個是除錯版本,內建所有除錯符號,使生活更容易除錯程式碼庫的C ++端。
另一個是經過優化的優化版本,載入和執行速度更快。

要使用除錯版本,載入build/artoolkit.debug.js並js/artoolkit.api.js進入您的頁面。該

artoolkit.api.js檔案包含與ARController和ARCameraParam的實現一起的C ++ < - > JS API繫結。

<script src="build/artoolkit.debug.js"></script>
<script src="js/artoolkit.api.js"></script>複製程式碼

結論

現在你知道JSARToolKit是一個在視訊和影像中跟蹤AR標記的庫。
您還知道AR標記是特殊影像,並且它們在JSARToolKit中有三種不同的型別。
您也可以知道,您可以使用Three.js在視訊中的AR標記之上繪製3D圖形(您當然知道如何使這種情況發生!因為您很棒,並且閱讀了程式碼示例!)

我希望你能夠採取這些瑣事,並使用它們來構建令人眼花繚亂的AR應用程式的未來,使每個人的生活變得更好,甚至無法想象會有多好(A LOT BETTER)。
或至少有樂趣製作一些AR行動網路應用程式。
不能低估發展中玩樂的重要性。





複製程式碼


相關文章