3Dモデリングソフトで制作したモデルデータの読み込み方を説明します。3Dのモデルデータにはさまざまな形式が存在しますが、Three.jsは多くの種類の形式に対応しています。

Three.jsでは外部ソフトを利用して作成した3Dモデリングデータを読み込むことができます。Three.jsではファイルを読み込むときにローダー(ファイルを解析する機能)を使ってモデルデータを読み込みます。

Three.jsが対応するモデルデータの形式

Three.jsでは次の形式の読み込みに対応しています。

形式名 説明 拡張子
glTF形式 インターネット向けの3Dファイル形式。2017年に仕様として定められた新しい形式。 .gltf, .glb
OBJ形式 Wavefront社のAdvanced Visualizerというソフト用のファイルフォーマット。テキストデータ。 .obj
Collada形式 汎用的なデータファイル。XMLで構成されている。 .dae
3DMax形式 Autodesk 3ds Maxの出力フォーマットとして使われるデータ形式。 .3ds

Three.jsでの読み込み方

Three.jsでモデルデータを読み込むには、JavaScriptでThree.jsの初期化を済ませたあとで、ローダーを使ってファイルを読み込み、3D空間に追加するという手順をとります。

データ形式ごとにローダークラスが用意されています。ただ、ローダークラスは、Three.jsライブラリの本体に含まれていないので注意が必要です。ローダークラスはscriptタグ等で取り込む必要があります。

GLTFファイルの場合

GLTF(ジーエルティーエフ)はインターネット向けの3Dファイル形式です。クロノスグループによって2017年に仕様として定められました。GLTFの中身はJSONファイルを中心として、そこから参照される画像やメッシュデータ等の関連ファイルで構成されています。

GLTFファイルのサンプルはクロノスグループのGitHubから取得できます。

今回は以下のファイルを利用します。ライセンスがPublic domain (CC0) のファイルです。

https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/ToyCar

gltfファイルの場合を読み込むにはGLTFLoader.jsファイルが必要となります。

以下のimportmapを記載して、three/addons/というエイリアスを貼ります。

<script type="importmap">
  {
    "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@0.167.0/build/three.module.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.167.0/examples/jsm/"
    }
  }
</script>

処理のほうではimport文で読み込みます。

<script type="module">
  import * as THREE from "three";
  import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";

  // …
</script>

読み込む処理は次のように記載します。GLTFLoaderクラスのインスタンスから、loadAsync()メソッドを利用します。 引数にはファイルパスを指定します。読み込み完了後に3D空間への追加処理をするのがポイントです。GLTFファイルにはシーンの情報の他に、カメラやライトなどさまざまな情報が含まれます。そのため、シーンの情報だけ抜き出すようにしましょう。

トップレベルで実行する場合

// GLTF形式のモデルデータを読み込む
const loader = new GLTFLoader();
// GLTFファイルのパスを指定
const gltf = await loader.loadAsync('./models/gltf/glTF/ToyCar.gltf');
// 読み込み後に3D空間に追加
const model = gltf.scene;
scene.add(model);

関数宣言をして実行する場合

// 非同期処理で待機するのでasync function宣言とする
async function init() {
  // ・・・省略

  // GLTF形式のモデルデータを読み込む
  const loader = new GLTFLoader();
  // GLTFファイルのパスを指定
  const gltf = await loader.loadAsync('./models/gltf/glTF/ToyCar.gltf');
  // 読み込み後に3D空間に追加
  const model = gltf.scene;
  scene.add(model);
  
  // ・・・省略
}

このコードの実行結果は次のとなります。

GLTFには、バイナリ形式のGLBがあります。GLTFはテキストデータや関連ファイルがばらけているのに対して、GLBはGLTFを1ファイルにまとめた形式となっています。GLBファイルを読み込む場合もGLTFLoaderクラスを利用します。コードは先ほど紹介したものとほとんど同じです。

// 非同期処理で待機するのでasync function宣言とする
async function init() {
  // ・・・省略

  // GLTF形式のモデルデータを読み込む
  const loader = new GLTFLoader();
  // GLTFファイルのパスを指定
  const objects = await loader.loadAsync('./models/gltf/binary/ToyCar.glb');
  // 読み込み後に3D空間に追加
  const model = objects.scene;
  scene.add(model);
  
  // ・・・省略
}

昔のThree.jsにloadAsync()メソッドは存在せず、load()メソッドのみ提供されていました。ネット上の記事では、load()メソッドで説明されているものが多いですが、Promiseによる非同期処理が苦手でなければawaitasyncを使って制御するといいでしょう。

3dsファイルの場合

3dsファイルの場合を読み込むにはTDSLoader.jsファイルが必要となります。CDNで読み込む場合は以下のscriptタグをHTMLに記述します。

<script type="importmap">
  {
    "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@0.167.0/build/three.module.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.167.0/examples/jsm/"
    }
  }
</script>

処理のほうではimport文で読み込みます。

<script type="module">
  import * as THREE from "three";
  import { TDSLoader } from "three/addons/loaders/TDSLoader.js";

  // …
</script>

読み込む処理は次のように記載します。TDSLoaderクラスのインスタンスから、loadAsync()メソッドを利用します。戻り値としてPromiseオブジェクトを返すのでawaitasyncで待機します。 引数にはファイルパスを指定します。

なお、3dsファイルのテクスチャーのパスがずれないように、setResourcePathメソッドを使って、明示的にテクスチャーが含まれるフォルダーのパスを指定します。

// 非同期処理で待機するのでasync function宣言とする
async function init() {
  // ・・・省略

  // 3DS形式のモデルデータを読み込む
  const loader = new TDSLoader();
  // テクスチャーのパスを指定
  loader.setResourcePath('models/3ds/portalgun/textures/');
  // 3dsファイルのパスを指定
  const object = await loader.loadAsync('models/3ds/portalgun/portalgun.3ds');
  // 読み込み後に3D空間に追加
  scene.add(object);

  // ・・・省略
}

このコードの実行結果は次のとなります。

Colladaファイルの場合

Colladaファイル(拡張子は.dae)の場合を読み込むにはColladaLoader.jsファイルが必要となります。

<script type="importmap">
  {
    "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@0.167.0/build/three.module.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.167.0/examples/jsm/"
    }
  }
</script>

処理のほうではimport文で読み込みます。

<script type="module">
  import * as THREE from "three";
  import { ColladaLoader } from "three/addons/loaders/ColladaLoader.js";

  // …
</script>

※Three.js r148(2022年12月リリース)よりexamples/jsフォルダーでの提供はなくなりました。今後はES Modulesでの利用を推奨されますので、本記事もゆくゆく更新します。

読み込む処理は次のように記載します。ColladaLoaderクラスのインスタンスから、loadAsync()メソッドを利用します。 引数にはファイルパスを指定します。読み込み完了後に3D空間への配置処理をするのがポイントです。Colladaファイルにはシーンの情報の他に、カメラやライトなどさまざまな情報が含まれます。そのため、シーンの情報だけ抜き出すようにしましょう。

// 非同期処理で待機するのでasync function宣言とする
async function init() {
  // ・・・省略

  // Collada形式のモデルデータを読み込む
  const loader = new ColladaLoader();
  // Colladaファイルのパスを指定
  const collada = await loader.loadAsync('./models/collada/elf/elf.dae');
  // 読み込み後に3D空間に追加
  const model = collada.scene;
  scene.add(model);
  // ・・・省略
}

このコードの実行結果は次のとなります。

まとめ

今回はモデルデータの読み込み方について説明しました。JavaScriptだけのコードだとどうしても表現がプログラミングアートよりになってしまうため、モデルデータを使った方が表現のバリエーションを増やせます。とくにキャラクターや建築物、物体の表示にはモデルデータの読み込みがかかせません。

関連

Node.js関連のバンドルツールで各種ローダーを利用する場合は、以下の解説を参照ください。