Communityコーディング&開発github.com

three.js glTFローディング

three.js r187 で glTF/GLB モデルをロードし、アニメーションを再生するスキル: GLTFLoader、DRACO・Meshopt・KTX2 デコーダー、AnimationMixer のクロスフェード、SkeletonUtils のクローン。モデルが見えない・小さい、デコーダファイルが404、アニメーションが再生されない、クローンがスケルトンを共有する場合に使用。シーン設定(`threejs-scene-setup`)やライティング(`threejs-materials-lighting`)には非対応。

three.js glTFローディング とは?

このスキルは、three.js(r187)でglTF/GLBモデルをロードし、アニメーションを再生するための包括的なガイドです。DRACO、Meshopt、KTX2などの圧縮ジオメトリやテクスチャに対応し、GLTFLoader、AnimationMixer、SkeletonUtilsを活用します。モデルが正しく表示されない、アニメーションが動かない、クローンがスケルトンを共有してしまうといった問題を解決するのに役立ちます。ファイル検証(`gltf-transform`)、デコーダのアタッチ、クロスフェード、名前付きパーツの検索、最適化のベストプラクティスをカバーし、デバッグ用のランタイムプローブも提供します。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mext-ai/elearning-agent-skills/tree/HEAD/third-party/gamedev/threejs-gltf-loading

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

three.js glTFローディング は何をしますか?

.gltf / .glb モデルをロードします(DRACO、Meshopt による圧縮ジオメトリ、KTX2 テクスチャを含む)。アニメーションクリップも再生できます。パターンは r187 を対象としています。既存プロジェクトでは、ユーザーがアップグレードを要求しない限り、固定されたリリースを維持します。glTF はデフォルトのランタイム形式です。OBJ はシーングラフやアニメーションを持たず、FBX はブラウザでの解析が重いためです。

使用するタイミング

  • モデルやキャラクターのインポート、ノード階層の調査、クリップの再生やブレンド、アニメーションする敵の多数コピーのスポーン、または表示されないモデルの修正。
  • ファイルが .gltf / .glb である場合、またはコードが three/addons/loaders/... から GLTFLoader、DRACOLoader、KTX2Loader をインポートしている場合。
  • レンダラー、カメラ、ループ(threejs-scene-setup)、モデルのライティングとマテリアル(threejs-materials-lighting)、DCCツールでのモデリングやエクスポート設定には使用しません。

ワークフロー

  1. ローダーコードを書く前にファイルを検査(gltf-transform が利用可能な場合): npx @gltf-transform/cli inspect model.glb を実行すると、メッシュ、拡張機能(例:KHR_draco_mesh_compression、EXT_meshopt_compression、KHR_texture_basisu)、クリップ、テクスチャサイズが表示されます。リストアップされた各圧縮拡張機能には、デコーダをアタッチする必要があります。
  2. GLTFLoader でロード(非同期コードでは loadAsync)。結果には gltf.scene(ルート Object3D)と gltf.animations(AnimationClip[])が含まれます。
  3. gltf.scene を追加し、フレーミングする。 glTF の単位はメートルです。名前と境界をログ出力して、コードが制御するパーツを見つけます。
  4. アニメーションのルートごとに1つの AnimationMixer、mixer.clipAction(clip) によるアクション、毎フレームの mixer.update(seconds)。
  5. 手順1で見つけた拡張機能のデコーダをアタッチする。 r185 以降、DRACOLoader と KTX2Loader はパスを指定しなくてもバンドルされたデコーダファイルを見つけます。
  6. 検証する 検証 を参照。

パターン

1. モデルのロードとフレーミング

import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const gltf = await new GLTFLoader().loadAsync('assets/robot.glb');
const root = gltf.scene;
scene.add(root);

const box = new THREE.Box3().setFromObject(root);
console.log('サイズ (m):', box.getSize(new THREE.Vector3()).toArray());
console.log('クリップ:', gltf.animations.map((c) => c.name));

2. クリップの再生

const timer = new THREE.Timer();            // THREE.Clock は r183 以降非推奨
const mixer = new THREE.AnimationMixer(gltf.scene);
const run = THREE.AnimationClip.findByName(gltf.animations, 'Run') ?? gltf.animations[0];
mixer.clipAction(run).play();

renderer.setAnimationLoop(() => {
  timer.update();
  mixer.update(timer.getDelta());           // 秒単位(ミリ秒ではない)
  renderer.render(scene, camera);
});

3. クリップ間のクロスフェード

const actions = Object.fromEntries(gltf.animations.map((c) => [c.name, mixer.clipAction(c)]));
let current = actions.Idle;
current.play();

function transitionTo(name, duration = 0.3) {
  const next = actions[name];
  if (!next || next === current) return;
  next.reset().play();                      // 巻き戻して古いフェードをクリア
  current.crossFadeTo(next, duration, false);
  current = next;
}

4. 圧縮ジオメトリとテクスチャ

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader, DRACO_GLTF_CONFIG } from 'three/addons/loaders/DRACOLoader.js';
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js';

const loader = new GLTFLoader()
  .setDRACOLoader(new DRACOLoader().setDecoderPath(DRACO_GLTF_CONFIG)) // glTF専用WASMビルド(r185+)
  .setKTX2Loader(new KTX2Loader().detectSupport(renderer))             // レンダラーが必要
  .setMeshoptDecoder(MeshoptDecoder);
// この1つのローダーをすべてのモデルで共有し、デコーダとワーカーが一度だけ作成されるようにする

r185 より前のリリース、またはホストがバンドルファイルを提供しない場合は、インストールされた three から examples/jsm/libs/draco/ と libs/basis/ をコピーし、それらのフォルダを setDecoderPath / setTranscoderPath に渡します。

5. 名前付きパーツの検索

const wheels = [];
gltf.scene.traverse((node) => {
  if (node.name.startsWith('Wheel')) wheels.push(node);
});
// ループ内: for (const w of wheels) w.rotation.x += dt * 4;

6. コードが必要なパーツを失わずにモデルを縮小

# Meshopt圧縮、最大1024pxのWebPテクスチャ、名前付きノードと階層を保持
npx @gltf-transform/cli optimize in.glb out.glb \
  --compress meshopt --texture-compress webp --texture-size 1024 \
  --flatten false --join-named false
npx @gltf-transform/cli validate out.glb

optimize はデフォルトで階層をフラット化し、名前付きのものを含むメッシュを結合します。これによりドローコールは減りますが、Wheel_FL を検索したりドアノードをアニメーションするコードが何も見つからなくなります。コードが操作するモデルでは、上記2つのフラグを保持してください。出力には setMeshoptDecoder(パターン4)が必要です。

検証

  1. ファイルチェック: 新しいまたは変更されたファイルごとに npx @gltf-transform/cli validate model.glb を実行し、ローダーコードをデバッグする前に報告されたエラーを修正します。
  2. ビルド: npm run build(または npx tsc --noEmit)が成功すること。
  3. ランタイムプローブ: scene とミキサーを window に公開します(Object.assign(window, { scene, mixer }))。この式を Playwright の page.evaluate に文字列として渡してページ内で実行させるか(threejs-scene-setup のスモークスクリプトが動作します)、またはユーザーにブラウザコンソールに await を付けて貼り付けてもらい、出力を報告してもらいます:
(async () => {
  const before = window.mixer?.time ?? null;
  await new Promise((r) => setTimeout(r, 500));
  const meshes = [];
  window.scene.traverse((o) => { if (o.isMesh) meshes.push(o.name || o.type); });
  return { meshes: meshes.length, mixerAdvanced: before !== null && window.mixer.time > before };
})();

メッシュ数が0以外で mixerAdvanced: true となることを期待し、スクリーンショットでスケールと向きを確認します。

落とし穴

  • モデルはロードされるが何も表示されない → PBRマテリアルにライトまたは環境がない(threejs-materials-lighting を参照)、またはスケールが100倍ずれている(センチメートルでのエクスポート)。パターン1からバウンディングボックスをログ出力します。
  • gltf がコールバックの外で未定義 → load は非同期です。loadAsync を使用するか、コールバック内で共有参照を代入します。
  • アニメーションが動かない → mixer.update が毎フレーム呼び出されていない、ミリ秒が渡されている、または play() が呼び出されていない。
  • draco_* または basis_* ファイルが404、またはデコードエラー → デコーダファイルが提供されていないか、別のthree.jsリリースからのものです。インストールされた three からコピーを提供し、ローダーにそのパスを指定します。
  • setKTX2Loader must be called before loading KTX2 textures または setMeshoptDecoder must be called before loading compressed files → ファイルが KHR_texture_basisu または meshopt 圧縮を使用しています。デコーダをアタッチします(パターン4)。
  • .hasFeature() called before the backend is initialized → KTX2Loader.detectSupport が WebGPURenderer 上で await renderer.init() の完了前に実行されました。
  • 同じモデルを2回追加すると移動する → Object3D は親を1つしか持ちません。クローンを作成し、スキンメッシュには SkeletonUtils.clone を使用します(リファレンスを参照)。
  • クローンが一緒にアニメーションする、または1つだけ動く → 単純な .clone() はスケルトンを共有します。クローンごとに SkeletonUtils.clone と個別のミキサーを使用します。
  • gltf-transform optimize 後に名前付きパーツが消える → デフォルトのフラット化と結合によりマージされました。--flatten false --join-named false を付けて再実行します。
  • ベイクされたオフセットがランタイム制御と競合する → エクスポーターはスケールや回転を子ノードにベイクできます。最初に名前とローカル変換をダンプし、クリーンなピボットのためにパーツを新しい Object3D の下に再ペアレント化するか、変換を適用した再エクスポートを依頼します。

リファレンス

  • references/loaders-and-animation.md には、複数のローダーをまたぐ LoadingManager プログレスバー、SkeletonUtils.clone を使用した多数のアニメーション敵のスポーン、AnimationAction のループとブレンドの詳細、デコーダホスティングの注意点、アーティストに渡すエクスポーターチェックリストが記載されています。

関連スキル

  • threejs-scene-setup: レンダラー、カメラ、ループ。
  • threejs-materials-lighting: PBRモデルが正しく見えるようにするためのライティングと環境。
  • create-game-assets: 3Dアセット自体のブリーフィング、ソーシング、チェック。
  • fps-shooter: これらのスキルを基盤とする3Dジャンル。

バージョンノート

最終確認日: 2026-11-11、three r187(npm [email protected]、公開日 2026-10-30)および @gltf-transform/cli 4.5.1 に対して。

  • r185: DRACOLoader と KTX2Loader がデフォルトでバンドルされたデコーダフォルダを使用するようになりました。DRACO_GLTF_CONFIG が追加されました。DRACOLoader.setDecoderConfig() は非推奨になりました(r194で削除予定)。r183: KHR_meshopt_compression が EXT_meshopt_compression と並んでサポートされるようになりました。r181: KTX2Loader.detectSupportAsync() が非推奨になりました。
  • gltf-transform 4.5.1 optimize のデフォルト: --compress meshopt、--flatten true、--join true、--join-named true、--simplify true、--texture-compress auto。--texture-compress ktx2 を使用するには、PATH 上に KTX-Software 4.4.0 以降の ktx コマンドが必要です。
  • ソース: r187 リリースノート、r187 DRACOLoader.js、r187 GLTFLoader.js、glTF Transform CLI、npx @gltf-transform/cli optimize --help。

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

関連スキル