みかづきブログ・カスタム

基本的にはちょちょいのほいです。

AR.js + Three.jsで画像認識時にコールバックイベントを発火させる(markerFoundの調整、markerLostの自作) 🔥

前回の検証 のイメージトラッキング(NFT)版です。
結論としては、前回の結果と全く同様でした。

ARとしてはARオブジェクトの位置を合わせるのが難しく、ちょっと実戦で使用するのは厳しいかなという印象だったイメージトラッキングですが、簡単な画像認証のような使い方であれば、それなりの精度で利用できそうです。
ただし、それなりの精度なので、似ている画像だとそれなりに誤認識が発生します。

DEMO

develop.kimizuka.org

今回はこちらの画像を認識させています。

https://m.media-amazon.com/images/I/51hjJ-AlVNL._SX343_BO1,204,203,200_.jpg

なので、「54字の物語」を撮影したときは「Found」、「54字の物語 怪」を撮影したときは「Lost」と表示されます。
画像認識時に右下にARオブジェクトが表示されるのですが、位置を合わせるのが難儀なので、これは非表示にしてしまい画像認証のような使い方をするのが現実的だと思いました。

ソースコード

index.html

<html>
<head>
  <title>three.js + ar.js</title>
  <meta name="viewport" content="width=device-width, viewport-fit=cover, shrink-to-fit=no" />
  <style>
    * {
      margin: 0; padding: 0;
    }

    .wrapper {
      position: relative;
      overflow: hidden;
    }

    .console {
      position: fixed;
      top: 8px;
      right: 8px;
      color: red;
    }
  </style>
</head>
<body>
  <div class="wrapper">
    <canvas></canvas>
    <p class="console"></p>
  </div>
  <script src="https://unpkg.com/three@0.127.0/build/three.min.js"></script>
  <script src="https://raw.githack.com/AR-js-org/AR.js/3.3.3/three.js/build/ar-nft.js"></script>
  <script>
    const renderer = new THREE.WebGLRenderer({
      canvas: document.querySelector('canvas'),
      antialias: true,
      alpha: true
    });
    const camera = new THREE.PerspectiveCamera();
    const scene = new THREE.Scene();
    const markerRoot = new THREE.Group();
    const arToolkitContext = new THREEx.ArToolkitContext({
      detectionMode: 'mono'
    });
    const arToolkitSource = new THREEx.ArToolkitSource({
      sourceType: 'webcam'
    });
    const arMarkerControls = new THREEx.ArMarkerControls(arToolkitContext, camera, {
      type: 'nft',
      // descriptorsUrl : 'arjs-nft/marker/54',
      changeMatrixMode: 'cameraTransformMatrix'
    });
    const lostDelay = 100;
    let markerState = 'lost';
    let timer = -1;

    renderer.setSize(window.innerWidth, window.innerHeight);
    scene.add(camera);

    window.addEventListener('resize', handleResize, {
      passive: true
    });

    window.addEventListener('arjs-nft-loaded', (evt) =>{
      console.log(evt);
    });

    arToolkitContext.init(() => {
      camera.projectionMatrix.copy(arToolkitContext.getProjectionMatrix());
    });

    arToolkitSource.init(() => {
      document.querySelector('.wrapper').appendChild(arToolkitSource.domElement);
      setTimeout(() => handleResize(), 400);
    });

    // マーカーが認識されている限り発火し続ける
    arMarkerControls.addEventListener('markerFound', () => {
      if (markerState === 'lost') {
        console.log('Found');
        document.querySelector('.console').innerText = 'Found';
      }

      markerState = 'found';

      clearTimeout(timer);
      timer = setTimeout(() => arMarkerControls.dispatchEvent({ type: 'markerLost' }),  lostDelay); // markerLostを発火させる
    });

    // 実装されていないので発火しない
    arMarkerControls.addEventListener('markerLost', () => {
      console.log('Lost');
      document.querySelector('.console').innerText = 'Lost';
      markerState = 'lost';
    });

    scene.visible = false;
    scene.add(markerRoot);

    const cube = new THREE.Mesh(
      new THREE.BoxGeometry(100, 100, 100),
      new THREE.MeshNormalMaterial()
    );

    cube.position.set(0, 0, 0);

    markerRoot.add(cube);

    renderer.setAnimationLoop((delta) => {
      if (arToolkitSource.ready) {
        arToolkitContext.update(arToolkitSource.domElement);
      }

      scene.visible = camera.visible;
      renderer.render(scene, camera);
    });

    function handleResize() {
      if (arToolkitSource.ready) {
        arToolkitSource.onResize();
        arToolkitSource.copySizeTo(renderer.domElement);
      }

      renderer.setPixelRatio(window.devicePixelRatio);
    }
  </script>
</body>
</html>