Three.jsでgltfpackモデルを読み込む

このツールの出力をGLTFLoaderで読み込むには、Meshopt圧縮用にMeshoptDecoder、KTX2テクスチャ用にKTX2Loaderを追加します。ファイルに必要な拡張機能は結果パネルで確認してください。

対応する依存パッケージをインストール

既存のViteアプリケーションなど、ESモジュールのバンドラーがあるnpmプロジェクトでこの例を使用してください。下記のバージョンは本サイトのビューアーと同じです。JavaScriptファイルをブラウザーで直接開くだけでは、パッケージ名のみのインポートは動作しません。

npm install [email protected] [email protected]

インストール済みThree.jsパッケージからBasisトランスコーダーのファイルをpublicディレクトリへコピーします。通常どおりpublicがサイトのルートで配信される構成では、下記のファイルに/basis/からアクセスできます。KTX2Loaderと同じThree.jsバージョンのファイルを使ってください。

node -e "require('fs').cpSync('node_modules/three/examples/jsm/libs/basis', 'public/basis', {recursive:true})"

読み込み前にデコーダーを接続

最適化済みファイルをpublic/models/optimized.glbに置き、idがmodelのcanvasを作ります。以下をアプリケーションのエントリーモジュールとして使ってください。読み込んだモデルの範囲にカメラを合わせ、アニメーションがあれば最初のものを再生します。

<canvas id="model"></canvas>
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
import { MeshoptDecoder } from 'meshoptimizer';

const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector('#model'), antialias: true,
});
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xeeeeee);
scene.add(new THREE.HemisphereLight(0xffffff, 0x444444, 3));
const light = new THREE.DirectionalLight(0xffffff, 3);
light.position.set(3, 5, 4);
scene.add(light);
const camera = new THREE.PerspectiveCamera(45, 1, 0.01, 1000);

const ktx2 = new KTX2Loader()
  .setTranscoderPath('/basis/')
  .detectSupport(renderer);
const loader = new GLTFLoader()
  .setMeshoptDecoder(MeshoptDecoder)
  .setKTX2Loader(ktx2);

function resize() {
  renderer.setSize(window.innerWidth, window.innerHeight);
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
}
window.addEventListener('resize', resize);
resize();

try {
  await MeshoptDecoder.ready;
  const gltf = await loader.loadAsync('/models/optimized.glb');
  scene.add(gltf.scene);
  const bounds = new THREE.Box3().setFromObject(gltf.scene);
  const center = bounds.getCenter(new THREE.Vector3());
  const radius = Math.max(bounds.getBoundingSphere(new THREE.Sphere()).radius, 0.01);
  const verticalFov = THREE.MathUtils.degToRad(camera.fov);
  const horizontalFov = 2 * Math.atan(Math.tan(verticalFov / 2) * camera.aspect);
  const distance = 1.2 * radius / Math.sin(Math.min(verticalFov, horizontalFov) / 2);
  camera.position.copy(center).add(new THREE.Vector3(1, 0.6, 1).normalize().multiplyScalar(distance));
  camera.near = radius / 100;
  camera.far = distance + radius * 10;
  camera.lookAt(center);
  camera.updateProjectionMatrix();

  const mixer = new THREE.AnimationMixer(gltf.scene);
  if (gltf.animations.length) mixer.clipAction(gltf.animations[0]).play();
  const clock = new THREE.Clock();
  renderer.setAnimationLoop(() => {
    mixer.update(Math.min(clock.getDelta(), 0.1));
    renderer.render(scene, camera);
  });
} catch (error) {
  console.error('Model loading failed:', error);
  const message = document.createElement('p');
  message.textContent = 'Model loading failed. Check the browser console and network requests.';
  document.body.prepend(message);
}

拡張機能と失敗したリクエストを確認

症状確認事項
MeshoptデコーダーのエラーloadAsyncの前にsetMeshoptDecoderを呼び、meshoptimizerパッケージをバンドルに含めてください。
KTX2テクスチャが読み込めないGLTFLoaderにKTX2Loaderを設定し、レンダラーを渡してdetectSupportを呼び、トランスコーダーのURLを確認してください。
モデルやWASMの代わりにHTMLが返るファイルが存在せず、アプリケーションのページにフォールバックしている可能性があります。HTTPステータス、応答内容、配信時のベースパスを確認してください。

厳格なContent Security Policyを設定したサイトでは、Web WorkersやWebAssemblyがブロックされていないかコンソールを確認します。workerとWASMの配信を意図して設定し、エラーを隠すためだけにポリシーを削除しないでください。ネットワーク応答が成功しても、デコード成功とは限りません。

この例は両方の出力を読み込めるよう、2つのデコーダーを設定しています。PNGやJPEGを保持するファイルにKTX2Loaderは不要です。WebPにはブラウザーとローダーの対応が必要ですが、Basisトランスコーダーは使いません。モデルを繰り返し切り替えるアプリケーションでは、古いジオメトリ、マテリアル、テクスチャを破棄し、不要になったKTX2ローダーも解放してください。

Three.js GLTFLoader · Three.js KTX2Loader

その他の出力互換性要件を確認 →

エンジンの資料: gltfpack

最適化ツールを開く ↗