A-FrameとAR.jsでマーカートラッキングをし、html2canvasで結果を画像保存するアプリを作ります。
現時点での最新バージョン(1.5.0)を動かしたかったのですが、スクリーンショットを撮った後にshader関連のエラーが出た為、過去バージョン(1.2.0)で行っています。
(1.3.0 , 1.4.0, 1.5.0ともにエラーでしたが、1度だけならスクショが撮れます)
ファイル構成
ローカルで実行する場合は同じフォルダ内に下記3ファイルを置いてください。
glitchなどのサーバー上で実行する場合はそれぞれ適宜フォルダ内に。
- index.htm : スクリプトを直接書く
- marker.patt : マーカー
- sample.glb : 3Dモデルのサンプル
マーカーの用意
AR.js Marker TrainingというサイトでARマーカーを作ります。
今回はQRコードをアップロードし作成しました。
Pattern Ratio を弄ると周りの黒枠のサイズが小さくなりますが、検知精度が落ちてしまうので今回は検知しやすいギリギリの0.8を選択しました。(Image sizeは512pxのまま)
設定がおわったらDownload Markerボタンを押すと.patt拡張子のマーカーファイルがダウンロードされます。

A-Frameの読み込み
バージョン0.6.0のマーカートラッキング実装サンプルは公式blogに載っていますが、このままバージョンだけ1.5.0にするとAR.jsのエラーが出るのでAR.jsライブラリもバージョンを変えます。
A-Frame対応のAR.jsライブラリはgithubページから”AR.js with Marker Tracking + Location Based AR (AFRAME)“のバージョンを選択
<script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>
<script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar.js"></script>
スクショを撮る機能を入れたい場合は、v1.5.0ではエラーが出るので下記のように1.2.0を読み込んでください
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script src="https://jeromeetienne.github.io/AR.js/aframe/build/aframe-ar.js"></script>
A-Seneを設置
<a-scene arjs="trackingMethod: best; sourceType: webcam; debugUIEnabled: false; patternRatio: 0.80;" embedded="" renderer="logarithmicDepthBuffer: true; precision: medium;" vr-mode-ui="enabled: false">
<!-- glbファイルの読み込み -->
<a-assets>
<a-asset-item id="glb-obj" src="sample.glb"></a-asset-item>
</a-assets>
<!-- マーカーの検出 -->
<a-marker preset="custom" type="pattern" url="sample.patt">
<a-entity rotation="0 0 0" scale="1,1,1" gltf-model="#glb-obj"></a-entity>
</a-marker>
</a-scene>
ボタンを設置
a-sceneの外側(</a-scene>の後)にボタンとキャンバスを置きます。
キャンバスはhtml2canvasが書き込むのでhiddenに設定しておきます。
<button id="screenshot-button"></button>
html2canvasでスクショを保存
a-sceneのgetCanvasではシーンに写っている3d modelだけしか映らないので、背景としてカメラ画像をhtml2canvasで合成します。
カメラ映像はAR.jsが動的に生成したvideoを使って取得します。
<script>
document.getElementById("screenshot-button").addEventListener("click", function() {
html2canvas(document.body).then(function(canvas) {
// カメラ画像を取得する為、AR.jsで展開されているvideoを取得
var camera = document.querySelector('video');
// 空のキャンバスを用意
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var width = camera.videoWidth;
var height = camera.videoHeight;
// A-Frameで描画しているSceneを取得
var aScene = document.querySelector("a-scene").components.screenshot.getCanvas("perspective");
if (width && height) {
// カメラのサイズをcanvasにセット
canvas.width = width;
canvas.height = height;
// canvasにカメラ画像を描いてからglbが映っているsceneを上書き
context.drawImage(camera, 0, 0, width, height);
context.drawImage(aScene, 0, 0, width, height);
// png画像を作成
var snap = canvas.toDataURL('image/png');
var link = document.createElement("a");
link.href = snap;
link.download = "screenshot.png";
//自動ダウンロード(クリック)
link.click();
}
});
});
</script>
サーバーを起動(ローカル環境のみ)
今回はpython3.8を使いました。
htmlのフォルダに移動してからコマンドラインで下記を打ち込みます。
python -m http.server 8000
ブラウザで http://localhost:8000/ のURLを開きます。
(※サーバーを起動しないとファイルを読み込む際にCORS policyに引っかかり表示されない場合があります)
カメラを向けて登録したマーカー(今回はQRコード)を検知すると3Dモデルが表示されるので、
丸ボタンを押すとスクショが撮影されダウンロードされてきます。

コード全体
index.htmlファイルの中身
<!DOCTYPE html>
<html lang="jp">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" name="viewport"/>
<!-- screenshot button用style-->
<style type="text/css">
#screenshot-button {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
width: 60px;
height: 60px;
border-radius: 50%;
background-color: #f3f3f3;
border: none;
cursor: pointer;
outline: none;
box-shadow: 0px 2px 5px rgba(0,0,0,0.2);
}
</style>
<title>A-Frame Marker Tracking sample</title>
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script src="https://jeromeetienne.github.io/AR.js/aframe/build/aframe-ar.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.3.2/html2canvas.min.js"></script>
</head>
<body>
<a-scene arjs="trackingMethod: best; sourceType: webcam; debugUIEnabled: false; patternRatio: 0.80;"
embedded=""
renderer="logarithmicDepthBuffer: true; precision: medium;"
vr-mode-ui="enabled: false">
<!-- glbファイルの読み込み -->
<a-assets>
<a-asset-item id="glb-obj" src="sample.glb"></a-asset-item>
</a-assets>
<!-- マーカーの検出 -->
<a-marker preset="custom" type="pattern" url="sample.patt">
<a-entity rotation="0 0 0" scale="1,1,1" gltf-model="#glb-obj"></a-entity>
</a-marker>
</a-scene>
<button id="screenshot-button"></button>
<script>
document.getElementById("screenshot-button").addEventListener("click", function() {
html2canvas(document.body).then(function(canvas) {
// カメラ画像を取得する為、AR.jsで展開されているvideoを取得
var camera = document.querySelector('video');
// 空のキャンバスを用意
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var width = camera.videoWidth;
var height = camera.videoHeight;
// A-Frameで描画しているSceneを取得
var aScene = document.querySelector("a-scene").components.screenshot.getCanvas("perspective");
if (width && height) {
// カメラのサイズをcanvasにセット
canvas.width = width;
canvas.height = height;
// canvasにカメラ画像を描いてからglbが映っているsceneを上書き
context.drawImage(camera, 0, 0, width, height);
context.drawImage(aScene, 0, 0, width, height);
// png画像を作成
var snap = canvas.toDataURL('image/png');
var link = document.createElement("a");
link.href = snap;
link.download = "screenshot.png";
//自動ダウンロード
link.click();
}
});
});
</script>
</body>
</html>