Three.js と glTF Web パイプライン は何をしますか?
このスキルは、Blender または vcad のモデルを軽量な .glb ファイルに変換する手順を説明します。このファイルは Three.js で高速にロードでき、簡単にアニメーションさせることができます。
1. Blender および vcad の書き出しチェックリスト
ノード名
- 可動部にはそれぞれ明確で安定した名前を付けます:
Head、Visor、Eye_L、Eye_R、Torso_Door、Wheel_FL、Wheel_FR、Steering_Wheel。 - Three.js では
model.getObjectByName("Torso_Door")でパーツを見つけます。
ピボット
- ルートの原点をモデルの底面中央に配置します。Three.js では、モデルが
Y = 0の地面に配置されます。 - 各可動部のピボットは回転軸上に配置します。例: ドアのヒンジ、ホイールの中心、首の付け根、アンテナの付け根。
トランスフォーム
- 書き出し前にすべてのトランスフォームを適用します。
- スケールは
1, 1, 1、回転は0, 0, 0にします。 - このルールにより、手続き的アニメーション中のドリフトやシアーを防ぎます。
ジオメトリ予算
- 同じマテリアルを共有する静的メッシュは結合します。この手順でドローコールが減ります。
- メッシュを個別に保持するのは、スクリプトで単独アニメーションさせる場合のみにします。ソフト変形には
SkinnedMeshとアーマチュアを使用します。 - ヒーローアセットは数千トライアングル、背景プロップは 1,000 トライアングル未満を目標とします。
2. @gltf-transform/cli による CLI 最適化
プロジェクトでは Bun を使用します。ツールは bunx で実行します。Node.js がインストールされているマシンでは npx でも動作します。
# メッシュ、テクスチャ、アニメーションの表示
bunx @gltf-transform/cli inspect model.glb
# Web 向け完全最適化: Meshopt + WebP + クリーンアップ
bunx @gltf-transform/cli optimize model.glb model-optimized.glb \
--compress meshopt \
--texture-compress webp \
--texture-size 2048
# 代替: Draco ジオメトリ圧縮
bunx @gltf-transform/cli draco model.glb model-draco.glb
Meshopt または Draco
- Meshopt (
EXT_meshopt_compression) は非常に高速にデコードします。頂点順序を保持し、アニメーションキーフレームやモーフターゲットも圧縮します。アニメーションアセットに使用します。 - Draco (
KHR_draco_mesh_compression) は、密な静的メッシュ(車体やスキャンデータなど)でファイルサイズを最小化します。デコーダの起動時コストが高くなります。
その他の便利な手順
# 重複メッシュと未使用データの削除
bunx @gltf-transform/cli dedup model.glb model.glb
# テクスチャを KTX2(BasisU)に変換
bunx @gltf-transform/cli etc1s model.glb model-ktx2.glb
各手順の後、ファイルサイズ、トライアングル数、テクスチャサイズを確認します。
3. Three.js 統合
Draco と Meshopt に対応した堅牢なローダー
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js';
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.7/');
const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);
gltfLoader.setMeshoptDecoder(MeshoptDecoder);
const gltf = await gltfLoader.loadAsync('/models/bender-optimized.glb');
const root = gltf.scene;
scene.add(root);
2 つのアニメーション手法
- ノードトランスフォームによる手続き的アニメーション。 ロボット、車両、その他の剛体パーツに使用します。例: ホイールの回転、ドアの開閉、ヘッドトラッキング、アンテナの揺れ。
const head = root.getObjectByName('Head');
const door = root.getObjectByName('Torso_Door');
const antenna = root.getObjectByName('Antenna');
function animate(time) {
if (antenna) {
antenna.rotation.z = Math.sin(time * 0.005) * 0.15;
}
if (head) {
head.rotation.y = THREE.MathUtils.lerp(head.rotation.y, targetYaw, 0.1);
}
}
THREE.AnimationMixerによるベイク済みクリップ。 Blender からエクスポートした複雑なサイクル(歩行、ダンス、フェイスモーフ)に使用します。
const mixer = new THREE.AnimationMixer(root);
const idleClip = THREE.AnimationClip.findByName(gltf.animations, 'Idle');
if (idleClip) {
mixer.clipAction(idleClip).play();
}
// レンダリングループ内:
// mixer.update(clock.getDelta());
金属パーツの PBR ライティング
金属マテリアル(metalness > 0.8)は Three.js では周囲に何もないと黒く見えます。環境マップを設定します:
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
const pmremGenerator = new THREE.PMREMGenerator(renderer);
scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture;
scene.environmentIntensity で反射の強さを調整します。金属アセットではトーンマッピングを THREE.ACESFilmicToneMapping に設定します。
パフォーマンスルール
- メッシュグループごとに 1 つのマテリアル。各マテリアルは 1 ドローコールになります。
- テクスチャを再利用します。小さなテクスチャは 1 つのアトラスにまとめます。
renderer.outputColorSpace = THREE.SRGBColorSpaceに設定します。glTF ローダーがテクスチャの色空間を設定します。- 同じアセットを複数回使う場合は
THREE.InstancedMeshを使用します。 - モデルを削除するときは解放します: シーンをトラバースし、
geometry.dispose()とmaterial.dispose()を呼び出します。