Communitygithub.com

zipang/Three.js と glTF Web パイプライン

3D アセット(.glb / .gltf)を最適化し、Three.js / React Three Fiber / GSAP / gltf-transform を組み合わせたインタラクティブな Web アニメーションに統合します。Draco・Meshopt・KTX2 による圧縮、ピボット・アニメーション設定、Web レンダリングに対応。

Three.js と glTF Web パイプライン とは?

「Three.js と glTF Web パイプライン」は、Blender や vcad で作成した 3D モデルを、軽量かつ高速にロード・アニメーション可能な .glb ファイルに変換し、Three.js 上で動作させるためのスキルです。このスキルは、3D アセットの書き出しチェックリスト、`@gltf-transform/cli` によるコマンドライン最適化、および Three.js との統合実装の 3 つの段階をカバーします。各段階では、ノード名の命名規則、ピボット設定、トランスフォームの適用、ジオメトリ予算の管理、Meshopt / Draco / KTX2 などの圧縮手法、Draco と Meshopt 両対応のローダー、手続き的アニメーションとベイク済みクリップの 2 つのアニメーション手法、PBR ライティング、パフォーマンス最適化ルールを詳細に解説します。このスキルを活用することで、開発者は 3D アセットを Web 向けに効率的に最適化し、スムーズなインタラクティブ体験を実現できます。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/zipang/bender-agent/tree/HEAD/.agents/skills/threejs-web

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

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

ドキュメント

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 つのアニメーション手法

  1. ノードトランスフォームによる手続き的アニメーション。 ロボット、車両、その他の剛体パーツに使用します。例: ホイールの回転、ドアの開閉、ヘッドトラッキング、アンテナの揺れ。
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);
  }
}
  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() を呼び出します。

関連スキル