使用JSARToolKit5 开发AR应用 (1) 简介

2,164 阅读11分钟

相关站点

jsartoolkit5 - ARToolkit.js

Talkingdata - 用数据的心智去超越

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


介绍

本文是使用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移动网络应用程序。
不能低估发展中玩乐的重要性。