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>
sam

sam

流山おおたかの森Techブログの管理人です。 お仕事のご依頼などはmail or Twitter[https://twitter.com/sam_sumario]で連絡頂けると反応出来ます。
Previous post DaVinci ResolveのPython api環境構築
Next post A-Frameで検出したマーカーの位置情報を取得する:コンポーネントシステム

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です