threejs-core-scene-graph について
threejs-core-scene-graphは、Three.jsのシーングラフを扱う開発者向けのリファレンススキルです。オブジェクト階層、変換、走査、レンダリングレイヤー、リソース管理上の注意点を解説します。
主な機能
- 01Object3Dの階層と変換
- 02Scene、Group、Meshの使い方
- 03走査とワールド行列の更新
- 04描画とレイキャスト用レイヤー
- 05シーンのフォグと環境設定
- 06リソース解放時の注意点
活用シーン
- 01Three.jsシーン内のオブジェクト整理
- 02親の変更時にワールド位置を維持
- 03フレーム途中でワールド座標を取得
- 04オブジェクトとGPUリソースの安全な管理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ impertio-studio/ three.js-claude-skill-package --skill threejs-core-scene-graphインストール後、Claude Code で/threejs-core-scene-graphを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
.gitkeepreferences/anti-patterns.mdexamples.mdmethods.md詳細
threejs-core-scene-graph
クイックリファレンス
シーングラフの階層
| クラス | 継承元 | 目的 |
|---|---|---|
Object3D | EventDispatcher | すべての 3D オブジェクトの基底クラスです。トランスフォーム、階層構造、走査機能を提供します |
Scene | Object3D | ルートコンテナーです。背景、環境、フォグ、overrideMaterial を追加します |
Group | Object3D | 追加機能を持たない、意味的なコンテナーです。論理的なグループ化に使用します |
Mesh | Object3D | ジオメトリ + マテリアルです。シーン内で主要な可視オブジェクトです |
Camera | Object3D | ビュープロジェクションです。行列を更新するため、必ずシーンに追加してください |
Light | Object3D | 照明です。レンダリングするため、必ずシーンに追加してください |
Object3D のコアプロパティ
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
position | Vector3 | (0,0,0) | 親に対するローカル位置 |
rotation | Euler | (0,0,0,'XYZ') | ローカル回転です。quaternion と連動しており、一方を変更すると必ずもう一方も更新されます |
quaternion | Quaternion | (0,0,0,1) | クォータニオンによるローカル回転です。rotation と連動しています |
scale | Vector3 | (1,1,1) | ローカルスケールです。不均一なスケールは法線の歪みを引き起こします |
visible | boolean | true | false の場合、レンダリング時にオブジェクトとすべての子孫がスキップされます |
layers | Layers | layer 0 | レンダリングとレイキャスティングを選択的に行うための 32 ビットビットマスクです |
castShadow | boolean | false | オブジェクトが影を落とすかどうか |
receiveShadow | boolean | false | オブジェクトが影を受けるかどうか |
frustumCulled | boolean | true | スカイボックスや必ずレンダリングする必要があるオブジェクトでは false に設定します |
renderOrder | number | 0 | 値が大きいほど後にレンダリングされます。透明度のソートに使用します |
name | string | "" | 人が読めるラベルです。検索には getObjectByName() を使用します |
userData | object | {} | アプリケーション固有のカスタムデータ辞書です |
識別プロパティ
| プロパティ | 型 | 説明 |
|---|---|---|
uuid | string | 読み取り専用の RFC 4122 v4 識別子です。自動生成されます |
id | number | 自動インクリメントされる整数です。実行セッションごとに一意です |
name | string | ユーザーが設定します。一意の識別子としては絶対に使用しないでください |
type | string | 読み取り専用のクラス名です(例:"Mesh"、"Group") |
重要な警告
children 配列を直接変更してはいけません。必ず add()、remove()、clear()、または attach() を使用してください。直接変更すると、内部管理(親参照、イベントディスパッチ)が壊れます。
traverse() の実行中に children 配列を変更してはいけません。オブジェクトを先に収集し、走査が完了してから変更してください。走査中に追加すると、予測不能な反復処理が発生します。
ワールド座標を維持する必要のあるオブジェクトの親を変更するときに、add() を使用してはいけません。代わりに必ず attach() を使用してください。add() はローカルトランスフォームを維持し、attach() はワールドトランスフォームを維持します。
同じフレーム内で位置/回転/スケールを変更した後、先に updateWorldMatrix(true, false) を呼び出さずに matrixWorld を読み取ってはいけません。次のレンダリングまたは明示的な更新が行われるまで、ワールド行列は古い状態です。
手動で破棄せずに Three.js オブジェクトを userData に保存してはいけません。dispose() は userData を自動的にはクリーンアップしません。
オブジェクトを削除するときに、テクスチャ、ジオメトリ、マテリアルの破棄を忘れてはいけません。remove() と clear() はシーングラフから切り離すだけで、GPU メモリを解放しません。
階層メソッド
add() と attach() の違い — 重要
import { Scene, Group, Mesh, BoxGeometry, MeshStandardMaterial } from 'three';
const scene = new Scene();
const groupA = new Group();
const groupB = new Group();
groupA.position.set(10, 0, 0);
groupB.position.set(0, 5, 0);
scene.add(groupA, groupB);
const mesh = new Mesh(new BoxGeometry(), new MeshStandardMaterial());
mesh.position.set(0, 0, 0);
groupA.add(mesh);
// mesh world position = (10, 0, 0) -- inherited from groupA
// WRONG: add() preserves LOCAL transform -- mesh jumps to (0, 5, 0) world
groupB.add(mesh); // auto-removes from groupA first
// CORRECT: attach() preserves WORLD transform -- mesh stays at (10, 0, 0) visually
groupA.add(mesh); // reset
groupB.attach(mesh); // mesh.position is recalculated to maintain world position
remove()、removeFromParent()、clear()
// Remove specific children
group.remove(meshA, meshB);
// Remove self from parent (safe when parent is null)
mesh.removeFromParent();
// Remove ALL children -- ALWAYS prefer over manual iteration
group.clear();
エッジケース: オブジェクトが子ではない場合でも、remove() は例外をスローせず、何もせずに終了します。
走査
// Depth-first traversal of ALL descendants
scene.traverse((object) => {
if (object.isMesh) {
object.castShadow = true;
}
});
// Skip invisible objects and their subtrees
scene.traverseVisible((object) => {
// only visits objects where visible === true
});
// Walk UP to root (does NOT include the starting object)
mesh.traverseAncestors((ancestor) => {
console.log(ancestor.name);
});
// Find by name, id, or arbitrary property
const wall = scene.getObjectByName('north-wall');
const obj = scene.getObjectById(42);
const selectable = scene.getObjectByProperty('userData', { selectable: true });
パフォーマンス: すべての検索メソッドは O(n) です。頻繁に検索する場合は、必ず参照をキャッシュしてください。
行列システム
自動モード(デフォルト)
レンダラーは各レンダリングの前にシーンで updateMatrixWorld() を呼び出し、再帰的に次の処理を行います。
- 各オブジェクトで
updateMatrix()を呼び出します(位置/回転/スケールからmatrixを合成します)。 matrixWorld = parent.matrixWorld * matrixを計算します。
手動モード(パフォーマンス最適化)
// For static objects -- skip per-frame matrix recomputation
object.matrixAutoUpdate = false;
object.matrix.compose(position, quaternion, scale);
object.matrixWorldNeedsUpdate = true;
フレームの途中でワールド空間の値を読み取る
import { Vector3, Quaternion } from 'three';
object.position.set(10, 0, 0);
object.updateWorldMatrix(true, false); // update parents first
const worldPos = new Vector3();
object.getWorldPosition(worldPos);
const worldQuat = new Quaternion();
object.getWorldQuaternion(worldQuat);
const worldScale = new Vector3();
object.getWorldScale(worldScale);
const worldDir = new Vector3();
object.getWorldDirection(worldDir);
座標変換
// MUTATION WARNING: both methods modify the input vector in-place
const localPoint = new Vector3(5, 0, 0);
object.localToWorld(localPoint); // localPoint is now in world coordinates
const worldPoint = new Vector3(15, 3, 0);
object.worldToLocal(worldPoint); // worldPoint is now in object's local coordinates
Scene クラス
import { Scene, Color, Fog, FogExp2, TextureLoader } from 'three';
const scene = new Scene();
scene.background = new Color(0x222222); // solid color
scene.environment = hdrTexture; // IBL for all PBR materials
scene.environmentIntensity = 1.5; // boost environment lighting
scene.environmentRotation.set(0, Math.PI, 0); // rotate environment
scene.fog = new Fog(0xcccccc, 10, 100); // linear fog
scene.overrideMaterial = depthMaterial; // debug: force all objects to one material
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
background | Color | Texture | CubeTexture | null | null | シーンの背景 |
environment | Texture | null | null | 独自の envMap を持たないすべての PBR マテリアル用の IBL 環境マップ |
fog | Fog | FogExp2 | null | null | シーンのフォグ効果 |
overrideMaterial | Material | null | null | すべてのオブジェクトにこのマテリアルを強制的に適用します |
backgroundBlurriness | number | 0 | 背景のぼかし(0~1) |
backgroundIntensity | number | 1 | 背景の明るさの倍率 |
backgroundRotation | Euler | (0,0,0) | 背景の回転 |
environmentIntensity | number | 1 | 環境マップの明るさの倍率 |
environmentRotation | Euler | (0,0,0) | 環境マップの回転 |
Fog
Fog(線形補間)
import { Fog } from 'three';
scene.fog = new Fog(0xffffff, 10, 200); // color, near, far
FogExp2(指数密度)
import { FogExp2 } from 'three';
scene.fog = new FogExp2(0xffffff, 0.01); // color, density
マテリアルとの相互作用: すべてのマテリアルには fog プロパティがあります(デフォルト:true)。ShaderMaterial と RawShaderMaterial のデフォルトは fog: false です。カスタムシェーダーにフォグを反映させるには、必ず fog: true を設定し、フォグのシェーダーチャンクを手動で含める必要があります。
Group
Group は追加機能を持たずに Object3D を拡張します。オブジェクトを整理するためのセマンティックなコンテナーとしてのみ存在します。
import { Group } from 'three';
const buildingGroup = new Group();
buildingGroup.name = 'building-01';
buildingGroup.add(walls, roof, foundation);
scene.add(buildingGroup);
// Transform all children together
buildingGroup.position.set(50, 0, 0);
buildingGroup.rotation.y = Math.PI / 4;
Mesh
Mesh は BufferGeometry と Material を組み合わせて、可視の表面を作成します。
import { Mesh, BoxGeometry, MeshStandardMaterial } from 'three';
const mesh = new Mesh(
new BoxGeometry(1, 1, 1),
new MeshStandardMaterial({ color: 0x00ff00 })
);
// Multi-material with geometry groups
const materials = [materialA, materialB];
geometry.addGroup(0, 36, 0); // start, count, materialIndex
geometry.addGroup(36, 36, 1);
const multiMesh = new Mesh(geometry, materials);
// Morph targets
mesh.morphTargetInfluences[0] = 0.5; // blend between base and morph target
Layers システム
選択的なレンダリングとレイキャスティングのための32ビットビットマスクシステムです。
import { Layers } from 'three';
// Objects are on layer 0 by default
mesh.layers.set(1); // ONLY layer 1 (removes from layer 0)
mesh.layers.enable(2); // add layer 2 (keep layer 1)
mesh.layers.disable(1); // remove layer 1
mesh.layers.toggle(3); // flip layer 3
// Camera renders only objects with overlapping layers
camera.layers.enable(1); // camera now sees layers 0 AND 1
// Test overlap
const visible = camera.layers.test(mesh.layers); // true if any layer overlaps
使用例:
- レイヤー0:デフォルトで表示されるオブジェクト
- レイヤー1:ヘルパー/ギズモ(本番環境のカメラでは無効にします)
- レイヤー2:ブルーム専用オブジェクト(選択的なポストプロセッシング)
- レイヤー3~31:カスタム用途(衝突グループ、選択セット、LOD グループ)
lookAt() の動作の違い
// Camera: points NEGATIVE-Z toward target (looks AT the target)
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);
// Non-camera objects: points POSITIVE-Z toward target
mesh.lookAt(targetPosition);
position を設定した後は、必ず lookAt() を呼び出してください。現在の位置から回転を計算します。
参考リンク
- references/methods.md -- Object3D、Scene、Group、Mesh、Layers の API シグネチャー全文
- references/examples.md -- 一般的なシーングラフ操作の実行可能なコード例
- references/anti-patterns.md -- シーングラフ管理で避けるべきこと
公式ソース
- https://threejs.org/docs/#api/en/core/Object3D
- https://threejs.org/docs/#api/en/scenes/Scene
- https://threejs.org/docs/#api/en/scenes/Fog
- https://threejs.org/docs/#api/en/scenes/FogExp2
- https://threejs.org/docs/#api/en/objects/Group
- https://threejs.org/docs/#api/en/objects/Mesh
- https://threejs.org/docs/#api/en/core/Layers