Three.jsで3Dオブジェクトにマウスがホバーしていた時、クリックした時を調べるにはレイキャストという機能を使います。次のサンプルでは、マウスが重なったオブジェクトだけ、赤くなるようにしています。

マウス位置をmousemoveイベントを使って監視します。マウス位置は-1.0から+1.0の割合で管理したいため、canvas要素の幅・高さやマウス座標から割合を数値計算します。

// canvas 要素の参照を取得する
const canvas = document.querySelector('#myCanvas');
// マウス座標管理用のベクトルを作成
const mouse = new THREE.Vector2();
// マウスイベントを登録
canvas.addEventListener('mousemove', handleMouseMove);

// マウスを動かしたときのイベント
function handleMouseMove(event) {
  const element = event.currentTarget;
  // canvas要素上のXY座標
  const x = event.clientX - element.offsetLeft;
  const y = event.clientY - element.offsetTop;
  // canvas要素の幅・高さ
  const w = element.offsetWidth;
  const h = element.offsetHeight;

  // -1〜+1の範囲で現在のマウス座標を登録する
  mouse.x = ( x / w ) * 2 - 1;
  mouse.y = -( y / h ) * 2 + 1;
}

レイキャストを利用するにはTHREE.Raycasterクラスを利用します。

上記のコードで求めたmouseオブジェクトを使って、レイキャストを更新しましょう。setFromCameraメソッドを使って、マウス位置からまっすぐに伸びる光線ベクトルに更新します。次に、その光線とぶつかったオブジェクトを得るためにintersectObjectsメソッドを利用します。すると、intersects配列に光線とぶつかったオブジェクトが格納されます。

※詳しい使い方は公式ドキュメントの「Raycaster」を参考ください。


// レイキャストを作成
const raycaster = new THREE.Raycaster();

tick();
// 毎フレーム時に実行されるループイベントです
function tick() {

  // レイキャスト = マウス位置からまっすぐに伸びる光線ベクトルを生成
  raycaster.setFromCamera(mouse, camera);

  // その光線とぶつかったオブジェクトを得る
  const intersects = raycaster.intersectObjects(scene.children);

  if(intersects.length > 0){
    // ぶつかったオブジェクトに対してなんかする
  }

  // レンダリング
  renderer.render(scene, camera);
  requestAnimationFrame(tick);
}

公式のexamplesにレイキャストのサンプルが用意されています。使い道を探ってみるといいでしょう。

まとめ

ちょっと難しいですが、応用の効く機能なのでぜひ覚えてみてください。