mcpField
T
threejs-core-raycaster

threejs-core-raycaster

github.com/impertio-studio開発23136 回インストール30 週間前に更新
リポジトリ

threejs-core-raycaster について

threejs-core-raycaster は、Three.js 開発者向けのレイキャスティング解説スキルです。ピッキング API、交差結果、性能面の注意点を扱い、オブジェクト選択の実装を支援します。

主な機能

  1. 01Raycaster の構築と設定
  2. 02カメラ座標からレイを生成
  3. 03オブジェクトを検査して交差情報を取得
  4. 04レイヤーによる対象の絞り込み
  5. 05結果配列の再利用による割り当て削減
  6. 06InstancedMesh のピッキング

活用シーン

  1. 01マウスでの選択やホバーの実装
  2. 02シーン内の操作対象の絞り込み
  3. 03ヒットしたインスタンスと番号の特定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/impertio-studio/three.js-claude-skill-package --skill threejs-core-raycaster

インストール後、Claude Code で/threejs-core-raycasterを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
anti-patterns.md
examples.md
methods.md

詳細

threejs-core-raycaster

クイックリファレンス

Raycaster コンストラクター
import * as THREE from 'three';

const raycaster = new THREE.Raycaster(
  origin,     // Vector3 — default: (0, 0, 0)
  direction,  // Vector3 — MUST be normalized, default: (0, 0, -1)
  near,       // number — minimum distance, default: 0
  far         // number — maximum distance, default: Infinity
);
プロパティ
プロパティ型デフォルト説明
rayRay--内部で使用する Ray オブジェクト(原点+方向)
nearnumber0最小交差距離
farnumberInfinity最大交差距離
cameraCamera--Sprite オブジェクトに対するレイキャストに必要です
layersLayerslayer 0レイヤーマスク -- 一致するレイヤー上のオブジェクトのみをテストします
paramsobject下記を参照型ごとの交差判定しきい値
Params のデフォルト値
raycaster.params = {
  Mesh: {},
  Line: { threshold: 1 },    // world units distance for line hits
  LOD: {},
  Points: { threshold: 1 },  // world units distance for point hits
  Sprite: {}
};
重要な警告

mousemove イベントのたびにスロットリングせずにレイキャストを実行してはなりません。複雑なシーンでは、これによりフレームレートが大幅に低下します。必ず requestAnimationFrame を使ってレンダリングループに合わせてスロットリングするか、チェック頻度を毎秒 30~60 回に制限してください。

ターゲットオブジェクトのフラットな配列がある場合、大規模なシーングラフに対して recursive: true を指定してはなりません。不要なツリー走査を避けるため、選択可能なオブジェクトのフラットな配列を必ず維持し、recursive: false を指定してください。

raycaster.set() を使う際、方向ベクトルの正規化を忘れてはなりません。正規化されていない方向を指定すると、すべての交差結果で距離の値が不正になります。

フレームごとに新しい結果配列を確保してはなりません。必ず optionalTarget パラメーターで配列を再利用し、処理後に array.length = 0 でクリアしてください。

操作可能なオブジェクトが一部だけの場合、シーン全体の scene.children に対してレイキャストを実行してはなりません。選択可能なオブジェクトの配列を別途必ず維持するか、レイヤーを使ってフィルタリングしてください。

Sprite オブジェクトに対してレイキャストする際、raycaster.camera を省略してはなりません。スプライトのスクリーン空間の境界を計算するには、レイキャスターにカメラの参照が必要です。


コアメソッド

set(origin, direction)

レイの原点と方向を手動で設定します。方向は必ず正規化してください。

const origin = new THREE.Vector3(0, 1, 0);
const direction = new THREE.Vector3(0, -1, 0); // already normalized
raycaster.set(origin, direction);
setFromCamera(coords, camera)

正規化デバイス座標(NDC)とカメラからレイを設定します。これは標準的なマウスピッキングの方法です。

  • coords:x と y が [-1, +1] の範囲にある Vector2
  • camera:PerspectiveCamera はカメラからその点を通るレイを生成し、OrthographicCamera は平行なレイを生成します
const mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
intersectObject(object, recursive?, optionalTarget?)

単一のオブジェクト(および任意でその子孫)との交差をテストします。

  • recursive(デフォルト:true):true の場合、すべての子孫をテストします
  • optionalTarget:確保を避けるために再利用する配列
  • 戻り値:Intersection[]。距離順(近い順)に必ずソートされます
intersectObjects(objects, recursive?, optionalTarget?)

オブジェクトの配列との交差をテストします。

  • recursive(デフォルト:true):true の場合、各オブジェクトの子孫もテストします
  • optionalTarget:確保を避けるために再利用する配列
  • 戻り値:Intersection[]。距離順(近い順)に必ずソートされます

Intersection オブジェクトの形式

返される配列内の各交差は、次の構造になります。

interface Intersection {
  distance: number;       // distance from ray origin to hit point
  point: Vector3;         // hit point in world space
  face: Face | null;      // hit face ({a, b, c} vertex indices + normal)
  faceIndex: number;      // index of hit face in the geometry
  object: Object3D;       // the intersected object reference
  uv?: Vector2;           // UV coordinates at intersection point
  uv1?: Vector2;          // second UV set (if available)
  normal?: Vector3;       // interpolated surface normal at hit point
  instanceId?: number;    // instance index (only for InstancedMesh)
}

オブジェクトにヒットしなかった場合、配列は空です。intersects[0] にアクセスする前に、必ず intersects.length > 0 を確認してください。


NDC への変換(正規化デバイス座標)

setFromCamera には、DOM のマウス座標を NDC に変換する必要があります。NDC 空間の範囲は -1(左/下)から +1(右/上)です。

// For fullscreen canvas (canvas fills entire window)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

// For non-fullscreen canvas (canvas has offset within page)
const rect = renderer.domElement.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

キャンバスがビューポート全体を占めていない場合は、必ず getBoundingClientRect() を使用してください。一部の領域にしか表示されないキャンバスで window.innerWidth/Height を使用すると、NDC の値が不正になり、ピッキング位置がずれます。


InstancedMesh のピッキング

InstancedMesh に対するレイキャストでは、交差結果にヒットしたインスタンスを識別する instanceId が含まれます。

import * as THREE from 'three';

const intersects = raycaster.intersectObject(instancedMesh);
if (intersects.length > 0) {
  const instanceId = intersects[0].instanceId;
  // Retrieve the instance's transform matrix
  const matrix = new THREE.Matrix4();
  instancedMesh.getMatrixAt(instanceId, matrix);

  // Example: change the color of the hit instance
  const color = new THREE.Color();
  instancedMesh.getColorAt(instanceId, color);
  instancedMesh.setColorAt(instanceId, new THREE.Color(0xff0000));
  instancedMesh.instanceColor.needsUpdate = true;
}

使用する前に、必ず instanceId !== undefined を確認してください。InstancedMesh 以外のオブジェクトにはこのプロパティがありません。


レイヤーによるフィルタリング

Raycaster は Layers システムに従います。レイヤーが raycaster.layers と重なるオブジェクトのみがテストされます。

// Assign objects to layers
selectableMesh.layers.set(1);        // exclusively on layer 1
decorationMesh.layers.set(2);        // exclusively on layer 2

// Configure raycaster to only test layer 1
raycaster.layers.set(1);
// Now intersectObjects skips all objects NOT on layer 1

// Enable multiple layers
raycaster.layers.enable(1);
raycaster.layers.enable(2);
// Now tests objects on layer 1 OR layer 2

// Reset to default (layer 0 only)
raycaster.layers.set(0);

レイヤーを使ってピッキンググループを作成します。インタラクティブなオブジェクトはレイヤー 1、インタラクティブではない装飾はレイヤー 2、ヘルパー/ギズモはレイヤー 3 に配置します。


パフォーマンスに関するガイドライン

  1. mousemove によるレイキャストを間引く -- mousemove イベントのたびにレイキャストを実行してはいけません。レンダーループ内で確認するフラグを使います。

    let needsRaycast = false;
    canvas.addEventListener('pointermove', (event) => {
      mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
      needsRaycast = true;
    });
    
    function animate() {
      if (needsRaycast) {
        raycaster.setFromCamera(mouse, camera);
        // perform intersection tests
        needsRaycast = false;
      }
      renderer.render(scene, camera);
      requestAnimationFrame(animate);
    }
    
  2. レイヤーを使う -- インタラクティブなオブジェクトを特定のレイヤーに割り当て、それに応じて raycaster.layers を設定し、インタラクティブではないジオメトリを完全にスキップします。

  3. 選択可能なオブジェクトのフラットな配列を維持する -- recursive: true を指定して scene に対してレイキャストするのではなく、別途 selectableObjects[] 配列を維持し、recursive: false を指定します。

  4. 交差配列を再利用する -- ガベージコレクションを避けるため、optionalTarget を渡します。

    const intersections = [];
    raycaster.intersectObjects(selectableObjects, false, intersections);
    // process results...
    intersections.length = 0;
    
  5. バウンディングスフィアによる事前チェック -- Three.js は三角形との交差判定の前に、バウンディングスフィアを自動的にテストします。geometry.computeBoundingSphere() が呼び出されていることを確認します(初回のレンダリング時に自動で呼び出されますが、初回のレンダリング前にレイキャストする場合は手動での呼び出しが必要です)。

  6. near/far を制限する -- 想定される交差距離がわかっている場合は、レイの範囲を狭めて遠くのオブジェクトを早い段階でスキップします。


参考リンク

公式ソース

コメント