使用 JSARToolKit 编写扩增实境应用

本文介绍了如何将 WebRTC getUserMedia API 和 JSARToolKit 库配合使用,以便在网络上运行增强现实应用。我会使用 WebGL 进行呈现,因为这可以提高性能。本文的最终结果是一款演示应用,该应用可在网络摄像头视频中的增强现实标记上显示一个 3D 模型。

JSARToolKit 是 JavaScript 增强现实库。这是一个根据 GPL 发布的开放源代码库,是我为 Mozilla 混合现实演示从 Flash FLARToolKit 直接移植而来。FLARToolKit 本身则移植自 Java NyARToolKit,而 Java NyARToolKit 又移植自 C ARToolKit。过程很复杂,但最后我们有了这个库。

JSARToolKit 要运行在画布元素上。由于该库需要从画布读取图片,因此图片需要与网页来源相同,或使用 CORS 避开同源政策。简而言之,在您要用作纹理的图片或视频元素上,将 crossOrigin 属性设置为 '''anonymous'

将画布传递给 JSARToolKit 进行分析后,JSARToolKit 会返回在图片和相应转换矩阵中发现的 AR 标记的列表。要在标记上绘制 3D 对象,请将转换矩阵传递给您使用的任何 3D 呈现库,以便通过矩阵转换对象。然后在 WebGL 场景中绘制视频帧,并在视频帧上绘制对象。准备完毕!

要使用 JSARToolKit 分析视频,请在画布上绘制视频,然后将画布传递给 JSARToolKit。在每一帧上都执行此操作后,视频增强现实跟踪就设置好了。在最新的 JavaScript 引擎上,JSARToolKit 的处理速度甚至足以在 640×480 的视频帧上实时执行此操作。但是,视频帧越大,处理所需的时间就越长。比较合适的视频帧尺寸为 320×240,但如果您想使用较小的标记或多个标记,那么最好使用 640×480。

演示

要查看网络摄像头演示,您需要在浏览器中启用 WebRTC(在 Chrome 浏览器中,请访问 about:flags 并启用 MediaStream)。您还需要打印出下方的增强现实标记。您也可以尝试在手机或平板电脑上打开标记图片,并将其置于网络摄像头的视野中。

增强现实标记。

以下就是我们的目标演示。此演示使用增强现实标记创建了图片幻灯片演示。将标记置于摄像头的视野中,系统就会在标记上显示一张照片。将标记移出摄像头的视野,然后再移入,图片就会变化。

增强现实网络摄像头演示(如果您没有 WebRTC,此处提供了一个预先录制的视频)

设置 JSARToolKit

JSARToolKit API 与 Java 很类似,因此您需要在使用时注意区分。基本概念就是,检测器对象运行在光栅对象上。摄像头参数对象位于检测器和光栅之间,用于将光栅坐标转换成摄像头坐标。要从检测器中获取检测到的标记,您应遍历这些标记,并将它们的转换矩阵复制到代码中。

第一步是创建光栅对象、摄像头参数对象和检测器对象。

// Create a RGB raster object for the 2D canvas.

// JSARToolKit uses raster objects to read image data.

// Note that you need to set canvas.changed = true on every frame.

var raster = new NyARRgbRaster_Canvas2D(canvas);

// FLARParam is the thing used by FLARToolKit to set camera parameters.

// Here we create a FLARParam for images with 320x240 pixel dimensions.

var param = new FLARParam(320, 240);

// The FLARMultiIdMarkerDetector is the actual detection engine for marker detection.

// It detects multiple ID markers. ID markers are special markers that encode a number.

var detector = new FLARMultiIdMarkerDetector(param, 120);

// For tracking video set continue mode to true. In continue mode, the detector

// tracks markers across multiple frames.

detector.setContinueMode(true);

// Copy the camera perspective matrix from the FLARParam to the WebGL library camera matrix.

// The second and third parameters determine the zNear and zFar planes for the perspective matrix.

param.copyCameraMatrix(display.camera.perspectiveMatrix, 10, 10000);

使用 getUserMedia 访问网络摄像头

接着,我要创建通过 WebRTC API 获取网络摄像头视频的视频元素。如果视频是预先录制的,只需将来源属性设置为视频网址即可。如果是在静态图片上检测标记,您就可以通过大体类似的方式使用图片元素。

由于 WebRTC 和 getUserMedia 仍属于新兴技术,因此您需要了解它们的功能。有关详情,请参阅埃里克·比德尔曼 (Eric Bidelman) 有关在 HTML5 中捕获音频和视频的文章。

var video = document.createElement('video');

video.width = 320;

video.height = 240;

var getUserMedia = function(t, onsuccess, onerror) {

if (navigator.getUserMedia) {

return navigator.getUserMedia(t, onsuccess, onerror);

} else if (navigator.webkitGetUserMedia) {

return navigator.webkitGetUserMedia(t, onsuccess, onerror);

} else if (navigator.mozGetUserMedia) {

return navigator.mozGetUserMedia(t, onsuccess, onerror);

} else if (navigator.msGetUserMedia) {

return navigator.msGetUserMedia(t, onsuccess, onerror);

} else {

onerror(new Error("No getUserMedia implementation found."));

}

};

var URL = window.URL || window.webkitURL;

var createObjectURL = URL.createObjectURL || webkitURL.createObjectURL;

if (!createObjectURL) {

throw new Error("URL.createObjectURL not found.");

}

getUserMedia({'video': true},

function(stream) {

var url = createObjectURL(stream);

video.src = url;

},

function(error) {

alert("Couldn't access webcam.");

}

);

检测标记

检测器运行正常后,我们就可以开始向它馈入要检测增强现实矩阵的图片了。先将图片绘制到光栅对象画布上,然后在光栅对象上运行检测器。检测器会返回在图片中找到的标记数量。

// Draw the video frame to the raster canvas, scaled to 320x240.

canvas.getContext('2d').drawImage(video, 0, 0, 320, 240);

// Tell the raster object that the underlying canvas has changed.

canvas.changed = true;

// Do marker detection by using the detector object on the raster object.

// The threshold parameter determines the threshold value

// for turning the video frame into a 1-bit black-and-white image.

//

var markerCount = detector.detectMarkerLite(raster, threshold);

最后一步是遍历检测到的标记并获取它们的转换矩阵。使用转换矩阵将 3D 对象显示在标记上。

// Create a NyARTransMatResult object for getting the marker translation matrices.

var resultMat = new NyARTransMatResult();

var markers = {};

// Go through the detected markers and get their IDs and transformation matrices.

for (var idx = 0; idx < markerCount; idx++) {

// Get the ID marker data for the current marker.

// ID markers are special kind of markers that encode a number.

// The bytes for the number are in the ID marker data.

var id = detector.getIdMarkerData(idx);

// Read bytes from the id packet.

var currId = -1;

// This code handles only 32-bit numbers or shorter.

if (id.packetLength <= 4) {

currId = 0;

for (var i = 0; i < id.packetLength; i++) {

currId = (currId << 8) | id.getPacketData(i);

}

}

// If this is a new id, let's start tracking it.

if (markers[currId] == null) {

markers[currId] = {};

}

// Get the transformation matrix for the detected marker.

detector.getTransformMatrix(idx, resultMat);

// Copy the result matrix into our marker tracker object.

markers[currId].transform = Object.asCopy(resultMat);

}

矩阵映射

以下代码用于将 JSARToolKit 矩阵复制转换成 glMatrix 矩阵(这些矩阵是 16 个元素的 FloatArrays,其中最后四个元素是转换列)。这段代码十分奇妙(也就是说,我不知道 ARToolKit 矩阵是如何设置的。可能是反转 Y 轴)。无论如何,这个符号反转技巧让 JSARToolKit 矩阵的运行效果与 glMatrix 相同。

要将该库与 Three.js 等其他库配合使用,您需要编写一个函数,用于将 ARToolKit 矩阵转换成相应库的矩阵格式。您还需要结合使用 FLARParam.copyCameraMatrix 方法。copyCameraMatrix 方法用于将 FLARParam 透视矩阵编写成 glMatrix 类型的矩阵。

function copyMarkerMatrix(arMat, glMat) {

glMat[0] = arMat.m00;

glMat[1] = -arMat.m10;

glMat[2] = arMat.m20;

glMat[3] = 0;

glMat[4] = arMat.m01;

glMat[5] = -arMat.m11;

glMat[6] = arMat.m21;

glMat[7] = 0;

glMat[8] = -arMat.m02;

glMat[9] = arMat.m12;

glMat[10] = -arMat.m22;

glMat[11] = 0;

glMat[12] = arMat.m03;

glMat[13] = -arMat.m13;

glMat[14] = arMat.m23;

glMat[15] = 1;

}

Three.js 集成

Three.js 是一款广受欢迎的 JavaScript 3D 引擎。我会向您介绍在 Three.js 中使用 JSARToolKit 输出的方法。您需要满足三个条件:上面绘制有视频图片的全屏四边形、支持 FLARParam 透视矩阵的摄像头,以及带有作为转换矩阵的标记矩阵的对象。我会通过以下代码向您介绍 Three.js 集成。

此连接指向有效的 Three.js 演示。该演示启动了调试输出,以便您查看 JSARToolKit 库的一些内部运行情况。

// I'm going to use a glMatrix-style matrix as an intermediary.

// So the first step is to create a function to convert a glMatrix matrix into a Three.js Matrix4.

THREE.Matrix4.prototype.setFromArray = function(m) {

return this.set(

m[0], m[4], m[8], m[12],

m[1], m[5], m[9], m[13],

m[2], m[6], m[10], m[14],

m[3], m[7], m[11], m[15]

);

};

// glMatrix matrices are flat arrays.

var tmp = new Float32Array(16);

// Create a camera and a marker root object for your Three.js scene.

var camera = new THREE.Camera();

scene.add(camera);

var markerRoot = new THREE.Object3D();

markerRoot.matrixAutoUpdate = false;

// Add the marker models and suchlike into your marker root object.

var cube = new THREE.Mesh(

new THREE.CubeGeometry(100,100,100),

new THREE.MeshBasicMaterial({color: 0xff00ff})

);

cube.position.z = -50;

markerRoot.add(cube);

// Add the marker root to your scene.

scene.add(markerRoot);

// Next we need to make the Three.js camera use the FLARParam matrix.

param.copyCameraMatrix(tmp, 10, 10000);

camera.projectionMatrix.setFromArray(tmp);

// To display the video, first create a texture from it.

var videoTex = new THREE.Texture(videoCanvas);

// Then create a plane textured with the video.

var plane = new THREE.Mesh(

new THREE.PlaneGeometry(2, 2, 0),

new THREE.MeshBasicMaterial({map: videoTex})

);

// The video plane shouldn't care about the z-buffer.

plane.material.depthTest = false;

plane.material.depthWrite = false;

// Create a camera and a scene for the video plane and

// add the camera and the video plane to the scene.

var videoCam = new THREE.Camera();

var videoScene = new THREE.Scene();

videoScene.add(plane);

videoScene.add(videoCam);

...

// On every frame do the following:

function tick() {

// Draw the video frame to the canvas.

videoCanvas.getContext('2d').drawImage(video, 0, 0);

canvas.getContext('2d').drawImage(videoCanvas, 0, 0, canvas.width, canvas.height);

// Tell JSARToolKit that the canvas has changed.

canvas.changed = true;

// Update the video texture.

videoTex.needsUpdate = true;

// Detect the markers in the video frame.

var markerCount = detector.detectMarkerLite(raster, threshold);

for (var i=0; i<markerCount; i++) {

// Get the marker matrix into the result matrix.

detector.getTransformMatrix(i, resultMat);

// Copy the marker matrix to the tmp matrix.

copyMarkerMatrix(resultMat, tmp);

// Copy the marker matrix over to your marker root object.

markerRoot.matrix.setFromArray(tmp);

}

// Render the scene.

renderer.autoClear = false;

renderer.clear();

renderer.render(videoScene, videoCam);

renderer.render(scene, camera);

}

摘要

在本文中,我们介绍了有关 JSARToolKit 的基本信息。现在,您可以通过 JavaScript 使用网络摄像头构建自己的增强现实应用了。

将 JSARToolKit 与 Three.js 集成有些麻烦,但却是完全可行的。我无法 100% 确定此演示中的操作正确与否,因此,如果您知道更好的实现集成的方法,请告诉我。

参考资料

  • JSARToolKit
  • Magi
  • Three.js

以上是 使用 JSARToolKit 编写扩增实境应用 的全部内容, 来源链接: utcz.com/z/264292.html

回到顶部