AR.jsでWebARしてみる(11) Cylinder between two markers

2020/10/17に公開

はじめに

WebARで分かったことメモしていく

参考

Cylinder between two markers (line2.html)を勉強する

デモ

  • 以下の環境で動作を確認
    • iOS 13.7 Safari
    • Android 9 Chrome
    • Windows10 Firefox 81.0.1

ソース

参考で紹介したサイトのCylinder between two markers (line2.html)のソース

<!doctype HTML>
<html>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<script src="js/aframe.min.js"></script>
<script src="js/aframe-ar.js"></script>
<body style="margin: 0px; overflow: hidden;">

<script>

// store visibility data in object;
//  can only draw line when both are visible.
let markerVisible = { m0: false, m1: false };

AFRAME.registerComponent('registerevents', {
    init: function () 
    {
        let marker = this.el;
        
        marker.addEventListener('markerFound', function() {
            markerVisible[ marker.id ] = true;
        });

        marker.addEventListener('markerLost', function() {
            markerVisible[ marker.id ] = false;
        });
    }
});

AFRAME.registerComponent('run', {
    init: function()
    {
        this.m0 = document.querySelector("#m0");
        this.m1 = document.querySelector("#m1");
        this.p0 = new THREE.Vector3();
        this.p1 = new THREE.Vector3(); 
        
        let geometry = new THREE.CylinderGeometry( 0.05, 0.05, 1, 12 );
        geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0.5, 0 ) );
        geometry.applyMatrix( new THREE.Matrix4().makeRotationX( THREE.Math.degToRad( 90 ) ) );
        let material = new THREE.MeshLambertMaterial( {color: 0xFF0000} );
        this.cylinder = new THREE.Mesh( geometry, material );
        this.cylinderGroup = document.querySelector('#cylinderGroup').object3D;
        this.cylinderGroup.add( this.cylinder );
    },
    
    tick: function (time, deltaTime) 
    {
        if ( markerVisible["m0"] && markerVisible["m1"] )
        {
            this.m0.object3D.getWorldPosition(this.p0);
            this.m1.object3D.getWorldPosition(this.p1);
            
            let distance = this.p0.distanceTo( this.p1 );
            this.cylinderGroup.lookAt( this.p1 );            
            this.cylinder.scale.set(1,1,distance);
            this.cylinder.visible = true;
        }
        else
        {
            this.cylinder.visible = false;
        }
    }
});

</script>

<a-scene embedded vr-mode-ui="enabled: false;" arjs="debugUIEnabled: false; detectionMode: mono_and_matrix; matrixCodeType: 3x3;">

    <a-assets>
	<img id="grid" src="images/border.png" />
    </a-assets>

    <a-marker type="barcode" value="0" id="m0" registerevents>
        <a-sphere radius="0.10" color="red"></a-sphere>
        <!-- this group rotates the cylinder to point at marker m1 -->
        <a-entity id="cylinderGroup"></a-entity>
    </a-marker>

    <a-marker type="barcode" value="1" id="m1" registerevents>
        <a-sphere radius="0.10" color="red"></a-sphere>
    </a-marker>
    
    <a-marker type="pattern" url="data/kanji.patt" id="baseMarker" ></a-marker>
    <a-entity camera></a-entity>
    <a-entity run></a-entity>
</a-scene>
</body>
</html>

確認したこと

処理概要

2つのマーカーが a-markerタグで設定されていて、両方のタグにregistereventsとある

<a-marker type="barcode" value="0" id="m0" registerevents></a-marker>
<a-marker type="barcode" value="1" id="m1" registerevents></a-marker>

registereventsに対しマーカーを見つけたときと見失ったときに処理が動くよう設定されている。

AFRAME.registerComponent('registerevents', {
    init: function () {
        let marker = this.el;
        marker.addEventListener('markerFound', function() { });
        marker.addEventListener('markerLost', function() { });
    }
});

2つのマーカーを見つけた時にシリンダーオブジェクトの形状を変形する。"2つのマーカー間の距離をシリンダーオブジェクトの長さにする", "シリンダーオブジェクトの向きを変更する" の2つを行う。

let distance = this.p0.distanceTo( this.p1 ); //マーカー間の距離を算出
this.cylinderGroup.lookAt( this.p1 ); //シリンダーの向きを設定(*1)
this.cylinder.scale.set(1,1,distance); // シリンダーの長さを設定
this.cylinder.visible = true; // シリンダーが見えるように設定
  • (*1) シリンダーの向きを設定 について
    • lookAtで、シリンダーオブジェクトの向きを設定してる。this.p1はもう1つのマーカーの中心ざ行のこと。つまりもう1つのマーカーに向かってシリンダーが伸びる感じになる。(へぇ~)

ちょっと改良してみた

棒の両端を立方体にしてみた。

ソースはこちら、立方体の向きをもう1つのマーカーに向けているのが工夫したポイント。

<!doctype HTML>
<html>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<script src="js/aframe.min.js"></script>
<script src="js/aframe-ar.js"></script>
<body style="margin: 0px; overflow: hidden;">

<script>
let markerVisible = { m0: false, m1: false };

AFRAME.registerComponent('registerevents', {
    init: function () 
    {
        let marker = this.el;        
        marker.addEventListener('markerFound', function() { markerVisible[ marker.id ] = true; });
        marker.addEventListener('markerLost',  function() { markerVisible[ marker.id ] = false; });
    }
});

AFRAME.registerComponent('run', {
    init: function()
    {
        this.m0 = document.querySelector("#m0");
        this.m1 = document.querySelector("#m1");
        this.p0 = new THREE.Vector3();
        this.p1 = new THREE.Vector3(); 
        
        let geometry = new THREE.CylinderGeometry( 0.05, 0.05, 1, 12 );
        geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0.5, 0 ) );
        geometry.applyMatrix( new THREE.Matrix4().makeRotationX( THREE.Math.degToRad( 90 ) ) );
        let material = new THREE.MeshLambertMaterial( {color: 0xFF0000} );
        this.cylinder = new THREE.Mesh( geometry, material );
        this.cylinderGroup = document.querySelector('#cylinderGroup').object3D;
        this.cylinderGroup.add( this.cylinder );

        this.cylinderGroup2 = document.querySelector('#cylinderGroup2').object3D;

    },
    
    tick: function (time, deltaTime) 
    {
        if ( markerVisible["m0"] && markerVisible["m1"] )
        {
            this.m0.object3D.getWorldPosition(this.p0);
            this.m1.object3D.getWorldPosition(this.p1);
            
            let distance = this.p0.distanceTo( this.p1 );
            console.log( "[debug] distance = " + distance );
            this.cylinderGroup.lookAt( this.p1 );
            this.cylinder.scale.set(1,1,distance);
            this.cylinder.visible = true;

            this.cylinderGroup2.lookAt( this.p0 );
        }
        else
        {
            this.cylinder.visible = false;
        }

    }
});

</script>

<a-scene embedded vr-mode-ui="enabled: false;" arjs="debugUIEnabled: false; detectionMode: mono_and_matrix; matrixCodeType: 3x3;">

    <a-marker type="barcode" value="0" id="m0" registerevents>
        <a-entity id="cylinderGroup">
            <a-box color="blue" depth="0.2" height="0.4" width="0.2" ></a-box>
        </a-entity>
    </a-marker>

    <a-marker type="barcode" value="1" id="m1" registerevents>
        <!-- <a-sphere radius="0.10" color="red"></a-sphere> -->
        <a-entity id="cylinderGroup2">
            <a-box color="green" depth="0.2" height="0.2" width="0.4" ></a-box>
        </a-entity>
    </a-marker>
    
    <a-entity camera></a-entity>
    <a-entity run></a-entity>
</a-scene>
</body>
</html>

Discussion