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ツールでのモデリングやエクスポート設定には使用しません。
ワークフロー
- ローダーコードを書く前にファイルを検査(
gltf-transformが利用可能な場合):npx @gltf-transform/cli inspect model.glbを実行すると、メッシュ、拡張機能(例:KHR_draco_mesh_compression、EXT_meshopt_compression、KHR_texture_basisu)、クリップ、テクスチャサイズが表示されます。リストアップされた各圧縮拡張機能には、デコーダをアタッチする必要があります。 GLTFLoaderでロード(非同期コードではloadAsync)。結果にはgltf.scene(ルートObject3D)とgltf.animations(AnimationClip[])が含まれます。gltf.sceneを追加し、フレーミングする。 glTF の単位はメートルです。名前と境界をログ出力して、コードが制御するパーツを見つけます。- アニメーションのルートごとに1つの
AnimationMixer、mixer.clipAction(clip)によるアクション、毎フレームのmixer.update(seconds)。 - 手順1で見つけた拡張機能のデコーダをアタッチする。 r185 以降、
DRACOLoaderとKTX2Loaderはパスを指定しなくてもバンドルされたデコーダファイルを見つけます。 - 検証する 検証 を参照。
パターン
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)が必要です。
検証
- ファイルチェック: 新しいまたは変更されたファイルごとに
npx @gltf-transform/cli validate model.glbを実行し、ローダーコードをデバッグする前に報告されたエラーを修正します。 - ビルド:
npm run build(またはnpx tsc --noEmit)が成功すること。 - ランタイムプローブ:
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。