見出し画像

[UIデザイナーが学ぶ]Three.jsの Raycasterで
マウスイベントを使う話

はじめに

この記事はthreejs journeyを利用しての学習メモです。
このthreejs journeyはthree.jsを学ぶのに非常に有用ですので、是非視聴してみてください。おすすめです!!


Raycasterとは


Three.jsで光線(Ray)を使ってオブジェクトとの交差を検出するためのものです。これを利用することで、特定の方向に向かって投げられた光線がシーン内のどのオブジェクトと交差するかを判定できます。

この機能は、例えばクリックした位置にあるオブジェクトを特定したり、オブジェクト同士の衝突を検出したりするのに利用できます。

やってみる

Raycasterの基本的な使い方です。

  1. Raycasterのインスタンスを作成

  2. Raycasterインスタンスのsetメソッドを利用して、原点の位置、Rayの向きを設定。
    Rayの向きは正規化しておく

  3. intersectObject、intersectObjectsメソッドを利用して対象がRayと交差しているか検出する

const raycaster = new THREE.Raycaster();
// 原点を決める
const rayOrigin = new THREE.Vector3(-3, 0, 0);
// 向きを決める
const rayDirection = new THREE.Vector3(10, 0, 0);
// 正規化する
rayDirection.normalize();
// セット
raycaster.set(rayOrigin, rayDirection);

const intersect = raycaster.intersectObject(object);

対象が複数ある場合。

raycaster.intersectObjects([object1, object2, object3]);


交差の結果は、1 つのオブジェクトだけをテストしている場合でも、常に配列になります。これは、光線が同じオブジェクトを複数回通過する場合があるからです。

Torusを以下のように通過した場合は、2回通過しますので結果は複数になります。

Torusを通過した場合

◎注意

注意点としては、レイキャストする前に、オブジェクトを更新しないと正しい結果が得られない場合があります。

// 座標の更新を行う
object.updateMatrixWorld();

const raycaster = new THREE.Raycaster();

const rayOrigin = new THREE.Vector3(-3, 0, 0);
const rayDirection = new THREE.Vector3(10, 0, 0);
rayDirection.normalize();
raycaster.set(rayOrigin, rayDirection);

const intersect = raycaster.intersectObject(object);

Raycasterとマウスイベント

マウスを利用した方法です。
こちらのやり方のほうが良く利用されるかもしれません。

Three.jsの公式ドキュメントのCode Exampleもこちらの方法が紹介されています。

マウスの座標と連携する

raycaster.setFromCameraを利用します。
raycaster.setFromCameraは、Three.jsでカメラとマウスの位置を基に光線を設定するためのメソッドです。

raycaster.setFromCamera(mouse, camera);
  • mouse
    スクリーン座標系でのマウスの位置を示すベクトルです(通常、xとyの値は-1から1の範囲)

  • camera
    シーンをレンダリングするために使用されるカメラオブジェクト

const raycaster = new THREE.Raycaster();

const mouse = new THREE.Vector2();

window.addEventListener("mousemove", (event) => {
  mouse.x = (event.clientX / sizes.width) * 2 - 1;
  mouse.y = -(event.clientY / sizes.height) * 2 + 1;
});

const clock = new THREE.Clock();

const tick = () => {
  const elapsedTime = clock.getElapsedTime();

  // 
  raycaster.setFromCamera(mouse, camera);

  const objectsTest = scene.children.filter(
    (child) => child instanceof THREE.Mesh
  );

 // 交差をテスト
  const intersects = raycaster.intersectObjects(objectsTest);

 // 一旦全てのオブジェクトを赤にする
 for (const object of objectsTest) {
    object.material.color.set("#ff0000");
  }

 // 交差した場合はオブジェクトの色を青にする
  for (const intersect of intersects) {
    intersect.object.material.color.set("#0000ff");
  }


  // Render
  renderer.render(scene, camera);

  // Call tick again on the next frame
  window.requestAnimationFrame(tick);
};

tick();

mouse enterとmouse leaveイベント

3次元空間だとmouse enter、mouse leaveイベントを取得することが難しいです。擬似的ですが以下の方法でmouse enter、leaveのタイミングを取得することができます。

const clock = new THREE.Clock();
let currentIntersect = null;

const tick = () => {
  const elapsedTime = clock.getElapsedTime();

  raycaster.setFromCamera(mouse, camera);

  const objectsTest = scene.children.filter(
    (child) => child instanceof THREE.Mesh
  );

  const intersects = raycaster.intersectObjects(objectsTest);
  for (const object of objectsTest) {
    object.material.color.set("#ff0000");
  }

  for (const intersect of intersects) {
    intersect.object.material.color.set("#0000ff");
  }

 // ここを追加
  if (intersects.length) {
    if(currentIntersect === null) {
        console.log("mouse enter")
    }
    currentIntersect = intersects[0];
  } else {
    if(currentIntersect) {
        console.log("mouse leave")
    }
    currentIntersect = null;
  }

  // Render
  renderer.render(scene, camera);

  // Call tick again on the next frame
  window.requestAnimationFrame(tick);
};

tick();

clickイベント

上記で取得したcurrentIntersectを利用してオブジェクトへのclickイベントも取得してみます。

window.addEventListener("click", () => {
  if (currentIntersect) {
    if (currentIntersect.object === object1) {
      console.log("object1");
    }
    if (currentIntersect.object === object2) {
      console.log("object2");
    }
    if (currentIntersect.object === object3) {
      console.log("object3");
    }
  }
});

まとめ

Raycasterを使いこなさないと、3Dを利用したインタラクティブなコンテンツは作れなそうです。
基礎的な部分はそこまで難しくないのでまずは使ってみて覚えていこうと思います。

いいなと思ったら応援しよう!