【A-Frame】Raycastで任意の地点の距離を測定して表示する

Pocket
LinkedIn にシェア
LINEで送る

A-FrameのRaycastで任意の距離感を測定したいなあと思ったのですが、始点はカメラ位置にしかできないみたいなので(そりゃそうか、VR用途だから。)、Three.js側のRaycastを使うことで対処できる。

デモ

画面中央の丸いポチをブロックに移動したりしてみてください。

ソースコード

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Raycasterサンプル</title>
    <meta name="description" content="Raycaster - A-Frame">
    <!-- <script src="../../../dist/aframe-master.js"></script> -->
    <script src="https://aframe.io/releases/1.0.0/aframe.min.js"></script>
    <script src="https://unpkg.com/aframe-button-controls@^1.1.0/aframe-button-controls.js"></script> 
  </head>
  <body>

<script>

  AFRAME.registerComponent('raycaster-listen', {
    init: function () {
      this.el.addEventListener('raycaster-intersected', evt => {
        // 箱の色を変える
        evt.target.setAttribute('material', 'color', '#7f7');
        // 交差情報を変数にセット
        this.raycaster = evt.detail.el;
        // 線を表示
        document.getElementById("cylinder").setAttribute('visible', true)    
      });

      // インターセクトから外れた
      this.el.addEventListener('raycaster-intersected-cleared', evt => {
        // 箱の色を戻す
        evt.target.setAttribute('material', 'color', '#f77');
        // 交差情報を空にする
        this.raycaster = null;
        // 線を消す
        document.getElementById("cylinder").setAttribute('visible', false)    
        // テキストを消す
        document.getElementById("text1").setAttribute('value', '') 
        // コメントを消す 
        document.getElementById("text_comment").setAttribute('visible', false) 
      });
    },
    // 毎フレーム処理する内容を書く
    tick: function () {
      if (!this.raycaster) { return; } // 見つからない

      var intersection = this.raycaster.components.raycaster.getIntersection(this.el);
      //document.getElementById("ball").setAttribute('position', intersection.point)    
      document.getElementById("cylinder").setAttribute('position', intersection.point);   

      //////////////////////
      // 任意の交差判定を作る
      // https://threejs.org/docs/#api/en/core/Raycaster
      var origin = intersection.point; //new THREE.Vector3(0, 0, -10); 
      var direction = intersection.face.normal; //new THREE.Vector3(1, 0, 0); 
      var near = 0.01;
      var far = 100;
      var ray = new THREE.Raycaster(origin, direction, 1);
      // 検索対象は自力で作ってみる
      var targetEls = this.el.sceneEl.querySelectorAll('.clickable');
      this.targets = [];
      for (var i = 0; i < targetEls.length; i++) {
        this.targets.push(targetEls[i].object3D);
      }
      // 交差判定を実行
      var intersection2 = ray.intersectObjects(this.targets, true);
      if (intersection2.length > 0) {
        console.log("あった!");
        document.getElementById("cylinder").setAttribute('visible', true) 

        // 円柱の位置と長さを求めて更新する
        var center = new THREE.Vector3(
          (origin.x + intersection2[0].point.x) / 2.0,
          (origin.y + intersection2[0].point.y) / 2.0,
          (origin.z + intersection2[0].point.z) / 2.0);
        document.getElementById("cylinder").setAttribute('position', center);
        document.getElementById("cylinder").setAttribute('height', intersection2[0].distance);

        // テキスト表示のサンプル
        document.getElementById("text1").setAttribute('visible', true) 
        document.getElementById("text1").setAttribute('value', String(intersection2[0].distance));
        var text_center = center;
        text_center.y += 0.6;
        document.getElementById("text1").setAttribute('position', text_center);
        // コメント表示
        document.getElementById("text_comment").setAttribute('visible', true) 
        var comment_center = center;
        comment_center.y -= 1;
        document.getElementById("text_comment").setAttribute('position', comment_center);
      } else {
        // 線を消す
        document.getElementById("cylinder").setAttribute('visible', false)   
        // テキストを消す 
        document.getElementById("text1").setAttribute('value', '') 
        // コメントを消す 
        document.getElementById("text_comment").setAttribute('visible', false) 
      }  
    }
  });

  AFRAME.registerComponent('clickevent', {
      init: function () {
        this.el.addEventListener('click', function (evt) {
          var state = document.getElementById("text_comment").getAttribute('value') 
          if (state.length== 0) {
            var text = prompt('コメントを入力ください');
            document.getElementById("text_comment").setAttribute('value', text);
          }
        });                
      }
  });
</script>

<!--https://aframe.io/docs/1.2.0/components/raycaster.html#sidebar-->
<!--
    テキスト表示、コメントのサンプル
-->

<a-scene>
  <a-entity camera look-controls wasd-controls></a-entity>

  <a-entity raycaster="objects: .clickable" cursor></a-entity>
  <a-entity id="raycaster" raycaster></a-entity>
  <a-entity geometry material raycaster-listen></a-entity>
  
  <a-entity id="box1" class="clickable" position="-5 0 -10" scale="2 4 3" geometry="primitive: box" material="color: #f77" raycaster-listen></a-entity>
  <a-entity id="box2" class="clickable" position="5 0 -10" scale="3 4 2" geometry="primitive: box" material="color: #f77" raycaster-listen></a-entity>
  <a-entity id="box3" class="clickable" position="15 5 -11" scale="5 6 4" geometry="primitive: box" material="color: #f77" raycaster-listen></a-entity>
  <a-entity id="box4" class="clickable" position="-20 5 -12" scale="6 4 3" geometry="primitive: box" material="color: #f77" raycaster-listen></a-entity>
      <a-entity position="0 0 0">
        <a-cylinder id="cylinder" position="0 0 0" rotation="90 0 90" height="5" radius="0.1" color="#f00" clickevent></a-cylinder>
      </a-entity>

      <!-- テキスト表示のサンプル -->
      <a-text id="text1" position="0 0 0" value="" scale="3 3 3" color="black" visible="false"></a-text>
      <a-text id="text_comment" position="0 0 0" value="" scale="3 3 3" color="blue" visible=""></a-text>
      
      <!-- next -->

      <!-- camera -->
      <a-camera position="0 0 0" collider-check>
      <!-- カーソル表示。画面中央のポッチ -->
      <a-cursor></a-cursor>
      </a-camera>      
</a-scene>

  </body>
</html>

 説明

レイキャストを2回やっています。
1回目のレイキャスト(this.raycaster.components.raycaster.getIntersection)はA-Frameコンポーネントを普通に使って、自分の視点から見つかった地点の座標を取得できます。
このとき、座標(intersection.point)と、面の法線(intersection.face.normal)を覚えておきましょう。

2回目のレイキャスト(new THREE.Raycaster(origin, direction, 1);)はTHREE.jsを使っています。先程見つけた座標から、法線方向にレイを飛ばせ という指示をします。

var intersection2 = ray.intersectObjects(this.targets, true)のところで、ぶつかる面を探しています。

任意の地点間の距離はintersection2[0].distanceです。

あとはこの視点~終点にたいして、可視化するためにCylinderを表現したりします。

参考サイト

・https://qiita.com/mkawamo/items/a0e6d962d1c716c75eae
・https://www.sejuku.net/blog/95030

この記事はお役に立ちましたか?
1
お気に召していただけましたか?

購入によるお支払(ご支援)も承っています。お値段は200円からお好きな価格を。

ご注意:返金のご対応はいたしかねます。

JPY

管理人が読んだおすすめの建築本

    

コメントを残す