LoginSignup
5
5

More than 3 years have passed since last update.

スマートフォンブラウザでAR(後半)

Last updated at Posted at 2020-05-26

はじめに

前回、ブラウザ上で3Dオブジェクトを表示する仕組みを作成しました。
続けてARの仕組みを作成します。
前半:https://qiita.com/NestVisual/items/a5030d4298cfbcb8b0af

カメラの初期化

WebRTCのMediaDevices.getUserMediaメソッドを用いてカメラを初期化します。
ここではカメラを初期化後、実際のカメラ解像度を取得し、video要素に映像を割り当てています。

sample2.html
<video id="myVideo" autoplay="true" muted playsinline="true" style="object-fit: cover;"></video>
    // (略)
    // カメラに要求するカメラ解像度
    var requireWidth = 480;
    var requireHeight = 640;
    var videoWidth;
    var videoHeight;
    var videoElement = document.getElementById('myVideo');

    var mode = "environment"; // インカメラを優先する場合 "user"
    var constraints = {
        video: {
            width: requireWidth,
            height: requireHeight,
            facingMode: mode,
        },
        audio: false
    };

    // getUserMedia成功時
    var onOpenStream = function(stream){
        // 実際のカメラ解像度を取得
        var track = stream.getTracks()[0];
        var setting = track.getSettings();
        videoWidth = setting.width;
        videoHeight = setting.height;
        videoElement.srcObject = stream;
        InitCanvas(); // 初期化処理の開始
    }

    // カメラの初期化
    if (navigator.mediaDevices.getUserMedia != null) {
        navigator.mediaDevices.getUserMedia(constraints)
            .then(function(stream) {
                onOpenStream(stream);
            })
            .catch(function(err){
                window.alert("カメラの初期化に失敗しました。端末およびブラウザの設定で、カメラへのアクセスが許可されているかを確認してください。");
            });
    }
    else{
        window.alert("このブラウザはWebRTCのカメラ初期化に対応していません。");
    }

ARの初期化

まず、こちらのサイトでマーカー画像とパターンファイルを作成します。
https://jeromeetienne.github.io/AR.js/three.js/examples/marker-training/examples/generator.html
screen.png

"Upload Marker"でマーカーとして使用する画像をアップロードし、
"Pattern Ratio"で、黒枠の太さを調整します。
pattern-marker.pattとpattern-marker.pngをダウンロードし、
pattファイルをhtmlから読める箇所に配置しておきます。
このスクリプトで、カメラがマーカー画像を読み込んだとき、オブジェクトが表示されるようにします。

sample2.html
    // arの初期化
    var arToolkitContext = new THREEx.ArToolkitContext({
        cameraParametersUrl: THREEx.ArToolkitContext.baseURL + '../data/data/camera_para.dat',
        detectionMode: 'color_and_matrix',
        matrixCodeType: '4x4_BCH_13_9_3',
        patternRatio: 0.80, // マーカー作成時のpatternRatioに合わせる
        maxDetectionRate: 10,
        canvasWidth: videoWidth,
        canvasHeight: videoHeight,
    })
    arToolkitContext.init(function onCompleted(){
    })

    // マーカーが読み込まれると表示されるオブジェクトを、シーンに追加します。
    var markerRoot = new THREE.Group();
    scene.add(markerRoot);
    var markerControls = new THREEx.ArMarkerControls(arToolkitContext, markerRoot, {
        type : 'pattern',
        patternUrl : "pattern-marker.patt",
        changeMatrixMode : 'modelViewMatrix',
        minConfidence: 0.50
    })

    var obj;
    var loader = new THREE.GLTFLoader();
    var url = "cube.glb";
    loader.load( url, function(data) {
        obj = data.scene;
        markerRoot.add( obj ); // マーカーで表示されるオブジェクトの下に配置します。
    }, undefined, function ( error ) {
        console.error( error );
    } );

サンプルコード

サンプルコード(折り畳み)
sample2.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>モデル表示サンプル</title>
</head>

<body style="margin : 0px; overflow: hidden;">
    <video id="myVideo" autoplay="true" muted playsinline="true" style="object-fit: cover;"></video>
    <canvas id="myCanvas" style="position: absolute; left: 0px; top:0px; z-index:2; pointer-events: none; "></canvas>

    <!-- Three.js (CDN) -->
    <script src="https://unpkg.com/three@0.110.0/build/three.min.js"></script>
    <script src="https://unpkg.com/three@0.110.0/examples/js/controls/OrbitControls.js"></script>
    <script src="https://unpkg.com/three@0.110.0/examples/js/loaders/GLTFLoader.js"></script>
    <!-- <script src="https://raw.githack.com/AR-js-org/AR.js/master/three.js/build/ar.js"></script> -->
    <script src="three.js/build/ar.min.js"></script>

    <script>
        //////////////////////////////////////////////////////////////////////////////////
        //      カメラの初期化
        //////////////////////////////////////////////////////////////////////////////////
        // カメラに要求するカメラ解像度
        var requireWidth = 640;
        var requireHeight = 480;

        var videoWidth;
        var videoHeight;

        var videoElement = document.getElementById('myVideo');

        var mode = "environment"; // インカメラを優先する場合"user"
        var constraints = {
            video: {
                width: requireWidth,
                height: requireHeight,
                facingMode: mode,
            },
            audio: false
        };

        // getUserMedia成功時
        var onOpenStream = function(stream){
            // 実際のカメラ解像度を取得
            var track = stream.getTracks()[0];
            var setting = track.getSettings();
            videoWidth = setting.width;
            videoHeight = setting.height;
            videoElement.srcObject = stream;
            InitCanvas();
        }

        // カメラの初期化
        if (navigator.mediaDevices.getUserMedia != null) {
            navigator.mediaDevices.getUserMedia(constraints)
                .then(function(stream) {
                    onOpenStream(stream);
                })
                .catch(function(err){
                    window.alert("カメラの初期化に失敗しました。端末およびブラウザの設定で、カメラへのアクセスが許可されているかを確認してください。");
                });
        }
        else{
            window.alert("このブラウザはWebRTCのカメラ初期化に対応していません。");
        }


        // キャンバスの初期化
        var InitCanvas = function(){
            //////////////////////////////////////////////////////////////////////////////////
            //      キャンバスの初期化
            //////////////////////////////////////////////////////////////////////////////////
            var canvasElement = document.querySelector('#myCanvas');
            //canvasElement.setSize( videoWidth, videoHeight );

            // renderer
            var renderer    = new THREE.WebGLRenderer({
                canvas: canvasElement,
                antialias: true,
                alpha: true
            });
            renderer.setClearColor(new THREE.Color('black'), 0)
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize( videoWidth, videoHeight );

            //////////////////////////////////////////////////////////////////////////////////
            //      シーンの初期化
            //////////////////////////////////////////////////////////////////////////////////
            var scene = new THREE.Scene();

            //////////////////////////////////////////////////////////////////////////////////
            //      arの初期化
            //////////////////////////////////////////////////////////////////////////////////
            var arToolkitContext = new THREEx.ArToolkitContext({
                cameraParametersUrl: THREEx.ArToolkitContext.baseURL + '../data/data/camera_para.dat',
                detectionMode: 'color_and_matrix',
                matrixCodeType: '4x4_BCH_13_9_3',
                patternRatio: 0.80, // マーカー作成時のpatternRatioに合わせる
                maxDetectionRate: 10,
                canvasWidth: videoWidth,
                canvasHeight: videoHeight,
            })
            arToolkitContext.init(function onCompleted(){
            })

            //////////////////////////////////////////////////////////////////////////////////
            //      マーカーが読み込まれると表示されるオブジェクトを、シーンに追加します。
            //////////////////////////////////////////////////////////////////////////////////
            var markerRoot = new THREE.Group();
            scene.add(markerRoot);
            var markerControls = new THREEx.ArMarkerControls(arToolkitContext, markerRoot, {
                type : 'pattern',
                patternUrl : "pattern-marker.patt",
                changeMatrixMode : 'modelViewMatrix',
                minConfidence: 0.50
            })

            //////////////////////////////////////////////////////////////////////////////////
            //      カメラの作成
            //////////////////////////////////////////////////////////////////////////////////
            const camera = new THREE.PerspectiveCamera(60, videoWidth / videoHeight, 0.01, 100); // 今回は原点配置のまま

            //////////////////////////////////////////////////////////////////////////////////
            //      マウス操作(OrbitControls)
            //////////////////////////////////////////////////////////////////////////////////
            // controls = new THREE.OrbitControls( camera, renderer.domElement );
            // controls.target.set( 0, 0, 0 );
            // controls.update();

            //////////////////////////////////////////////////////////////////////////////////
            //      オブジェクトのロード
            //////////////////////////////////////////////////////////////////////////////////
            var obj;
            var loader = new THREE.GLTFLoader();
            var url = "cube.glb";
            loader.load( url, function(data) {
                obj = data.scene;
                markerRoot.add( obj ); // マーカーで表示されるオブジェクトの下に配置します。
            }, undefined, function ( error ) {
                console.error( error );
            } );

            //////////////////////////////////////////////////////////////////////////////////
            //      ライティング
            //////////////////////////////////////////////////////////////////////////////////
            // 平行光源を作成
            const directionalLight = new THREE.DirectionalLight(0xFFFFFF);
            directionalLight.position.set(1, 1, 5);
            scene.add(directionalLight);
            // 環境光を追加
            const light = new THREE.HemisphereLight(0xFFFFFF, 0x333333, 2.0);
            light.position.set(1, 5, 1);
            scene.add(light);

            //////////////////////////////////////////////////////////////////////////////////
            //      毎フレームの描画処理
            //////////////////////////////////////////////////////////////////////////////////
            drawFrame();
            function drawFrame() {
                // 映像を解析してARを実行
                arToolkitContext.update( videoElement );

                renderer.render(scene, camera);
                requestAnimationFrame(drawFrame);
            }
        }
    </script>
</body>

</html>

まとめ

このようにして、スマホでARが出来るようになりました。
screen2.png

5
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5