Blenderで出力した、GLTF(GL Transmission Format)形式と、3Dモデルが配布される際に良く用いられるVRM(Virtual Reality Model)形式について、Three.jsを用いたブラウザ上での表示方法を備忘録的にまとめておきます。


どちらも形式のファイルも同じように読み込んで、表示することが可能です。


目的:ブラウザで3Dモデルを表示


本ページでは下のように3Dモデルを読み込んで、ブラウザ上で表示するところまでの手順と、設定方法について見ていきます。

マウスのクリック&ドラッグや、ホイール、スマホであればスクロールなどでカメラの位置や拡大縮小を制御できるようにしているので、折角なので360°から見てみてください。
このように、webブラウザ上でリアルタイムレンダリングして、3Dモデルを表示することができます。

ネコ後輩
ブラウザコンテンツの表現の幅が広がる~♪


3Dモデル表示に仮定する環境


ひとまず、前提となる3Dモデルの用意と開発環境について説明します。

3Dモデルの用意

上の本の3Dモデルは、Blender(3DCG作成ツール)で一から作成しました。

▼Blenderを用いたモデル作成手順


作成したモデルは特定のフォーマットにエクスポートすることで、Three.jsなどを用いて容易にロードすることができます。今回は以下のようにgltf形式でエクスポートします。

「File」→「Export」→「glTF2.0(.glb/.glth)」

下のようにエクスポートするときに、詳細な設定ができます。もともと軽量なgltf形式ですが、必要のないデータをファイル中に含めないよう設定を変更することで、より軽量なデータとなり、扱いやすくなります。


もちろん初期設定のままでも構いません。今回は細かな調整はせず、エクスポートしたため、上の本のファイルの大きさは、63KBほどとなりました。

ネコ後輩
早く3Dモデルを読み込む方法だけ知りたいで~す!
タコ先輩
簡易的にモデルを用意したいなら、
3Dモデルを無料で配布しているサイトで拾ってくると便利だぞ!


仮定する環境:フォルダツリー

3Dモデルを読み込む方法が分かりやすくなるように、できるだけシンプルで、単純な構成を仮定して話を進めていきます。

rootフォルダに置かれる表示のTopとなるhtml(index.html)の中にThree.jsを含むjavascriptを記述し、同じ階層に3Dモデルを置いた以下の構成を例に3Dモデル読み込みから、表示までの方法をまとめます。
├── book_1204.glb   // 3D model (gltfファイル)
├── AliciaSolid.vrm // 3D model (vrmファイル)
└── index.html      // topとなるhtml
npmやwebpack、typescriptを用いた、より実践に近い開発環境を含む構成については、GitHubへのリンクをこの記事の最後に貼っているので、そちらをご覧ください。

また、実装時の注意点としてjavascriptはクロスドメイン制約という制約があります。3Dモデルは「https://ww....model.glb」のようにブラウザで異なるオリジン(ドメイン)の場所からデータをアクセスしてくることができないようになっています。

ですので、本記事で紹介する方法を試すときは、本記事で使用しているモデルのパスを使用しても使うことができません。必ず同じドメイン内(フォルダ内)にモデルを作成、もしくはダウンロードして配置する必要がありますので、そこのところ少しお気をつけて。

ネコ後輩
これに気が付かず、数時間ほどモデルが表示できないエラーに苦しんだ;;


3Dモデルの読み込み方法


基本的にgltf形式のファイルもvrm形式のファイルも同じLoaderを使って読み込むことができます
以下のコード抜粋箇所は指定するファイルを変えているだけです。

sceneは、「scene = new THREE.Scene();」を指します。

gltf(glb)形式ファイルの読み込み

Blenderからエクスポートしたgltf(GL Transmission Format)ファイル「book_1204.glb」は以下のように読み込みます。
/* glb/vrmファイルの読み込み */
const loader = new GLTFLoader();
loader.load ('./book_1204.glb', function(gltf){
  scene.add(gltf.scene);                              // モデルをシーンに追加
}, undefined, function (error) {
  console.error(error);
}); 
「scene.add(gltf.scene);」の箇所でシーンにモデルを追加して表示することができます。

vrm形式ファイルの読み込み

VRM(Virtual Reality Model)形式のファイル「AliciaSolid.vrm」についても、GLTF形式のファイルと同じローダーを用いて読み込むことが可能です。

VRM形式は、gltf形式をベースにした形式なので、同じようにモデルを読み込むことができるということですね。
/* glb/vrmファイルの読み込み */
const loader = new GLTFLoader();
loader.load ('./AliciaSolid.vrm', function(gltf){
  scene.add(gltf.scene);                              // モデルをシーンに追加
}, undefined, function (error) {
  console.error(error);
}); 

上記の本の例は、gltf形式のファイルでしたが、以下は、ニコニ立体からダウンロードしてきたvrm形式のファイル(AliciaSolid.vrm)を読み込んだ時の表示例です。


ネコ後輩
モデルの読み込みはできたけど、サイズが思っていたものと違うんですが、、
タコ先輩
次の記述を追加して、サイズや配置を変えることができるぞ!

Draco圧縮されたgltf形式ファイルの読み込み

最近データサイズの軽量化のために頻繁に用いられる3Dモデルの圧縮形式としてDracoというものがあります。

この形式で圧縮されたgltf形式のファイルを読み込む場合には、GLTFLoaderに加えてDRACOLoderというローダーを使用しなければいけません

この圧縮されたファイルを扱う場合は以下の記事などをご覧ください。

▼関連記事▼



3Dモデルの表示・配置方法


モデルの配置やサイズは以下のように調節します。Blender上で表示サイズを気にせずモデル作っても、後から表示サイズを変更することができるので安心です。

大きさ・位置・角度の調節

基本的に、Three.js上でオブジェクトを追加したときと同じようにサイズや位置を調節できます。

少し注意したいのは、角度はラジアンで指定しなけれないけないので、90°回転したければ(90 * Math.PI / 180)のように計算します。

下の例では、回転していないのでデフォルトの配置からx,y,z軸に0°の回転を指定しています。
/* glb/vrmファイルの読み込み */
const loader = new GLTFLoader();
loader.load ('./book_1204.glb', function(gltf){
  gltf.scene.scale.set(6, 6, 6);                      // モデルスケール変更
  gltf.scene.position.set(0, 0, 0);                   // モデル位置変更
  gltf.scene.rotation.set(0, (0 * Math.PI /180), 0);  // モデル向き変更
  scene.add(gltf.scene);                              // モデルをシーンに追加
}, undefined, function (error) {
  console.error(error);
}); 

以下のコードなどを使って上記の値を変えて、見え方がどのように変わるか試してみると感覚が掴めるかと思います。


3Dモデルを表示するコード全体


HTMLもJavascriptの実装部分も一つのファイルにまとめた簡単な構成で、コードの全体を載せておきます。環境構築も面倒な手間がないように、CDN(Contents Delivery Network)から、必要なライブラリを読み込んでいます。

実際に動作確認まで済ませてあるので、CDNの配信停止などない限り動くはずです。唯一変更が必要な個所は「./book_1204.glb」のモデル指定部分です。各自のモデルのフォルダ上の配置と名前に合わせて変更してください。

html:
<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>gtlf load</title>
    <style>
      body {
        /*ページ全体を使用するためmarginを0に設定
            overflowをhiddenに設定*/
        margin: 0;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <!-- (表示) 出力を保持する -->
    <div id="WebGL-output"></div>
    <!-- Three.jsコードの記述 -->
    <script type="module">
      // CDNからThree.js読み込み
      import * as THREE from'https://unpkg.com/three@0.126.1/build/three.module.js';
      import { OrbitControls } from 'https://unpkg.com/three@0.126.1/examples/jsm/controls/OrbitControls.js';
      import { GLTFLoader } from 'https://unpkg.com/three@0.126.1/examples/jsm/loaders/GLTFLoader.js';
      var camera;       // カメラ
      var scene;        // シーン
      var renderer;     // レンダラー
      var controls;     // カメラコントロール

      /* すべての読み込みが終わってからThree.js関連の処理を実行 */
      function init() {
        // オブジェクト、カメラ、ライトなどすべての要素を格納するシーン作成 
        scene = new THREE.Scene();
        scene.fog = new THREE.FogExp2(0xffffff, 0.005); // 遠くの物が霞んで見える設定
        // カメラ作成
        camera = new THREE.PerspectiveCamera(
          45,                                     // 視野
          window.innerWidth / window.innerHeight, // アスペクト比
          0.1,                                    // どの程度のカメラの距離から描画を始めるか
          1000                                    // どのくらい遠くまで見えるか
        );
        scene.add(camera);                        // カメラをシーンに追加
        camera.position.x = -30;
        camera.position.y = 40;
        camera.position.z = 30;
        camera.lookAt(scene.position);            // シーンの中心にカメラを向ける

        // レンダラー作成
        renderer = new THREE.WebGLRenderer();
        renderer.setClearColor(new THREE.Color(0xeeeeee));
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMap.enabled = true;        // オブジェクトシャドウを有効化
        
        // 均等光源(影なし)
        var ambientLight = new THREE.AmbientLight(0x0c0c0c, 1);
        scene.add(ambientLight);
        // 点座標への光源(影あり)
        var spotLight = new THREE.SpotLight(0xffffff, 1);
        spotLight.position.set(-20, 30, -5);
        spotLight.castShadow = true; // 影を落とす
        scene.add(spotLight);
        // 無限遠からの平行光源
        var directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
        directionalLight.position.set(20, -30, 5).normalize(); // 光源方向設定
        scene.add(directionalLight);

        /* glbファイルの読み込み */
        const loader = new GLTFLoader();
        loader.load ('./book_1204.glb', function(gltf){
          gltf.scene.scale.set(6, 6, 6);                      // モデルスケール変更
          gltf.scene.position.set(0, 0, 0);                   // モデル位置変更
          gltf.scene.rotation.set(0, (0 * Math.PI /180), 0);  // モデル向き変更
          scene.add(gltf.scene);                              // モデルをシーンに追加
        }, undefined, function (error) {
          console.error(error);
        }); 

        // レンダラーの出力をhtmlの(WebGL-output)に追加
        document
          .getElementById("WebGL-output")
          .appendChild(renderer.domElement);

        // OrbitControlsを初期化し、カメラとレンダラーを渡す
        controls = new OrbitControls(camera, renderer.domElement);
        controls.enableRotate = true; // カメラの回転を有効化

        // シーンを描画
        render();
      }

      // シーンを描画する関数
      function render() {
          requestAnimationFrame(render);  // レンダリング(再帰)
          renderer.render(scene, camera); // 表示
      }
      
      // 表示領域をウィンドウサイズに合わせる
      function onResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
      }
      // 表示が終わってからThree.js関連処理(関数init)を実行
      window.addEventListener("load", init);
      // リサイズイベント
      window.addEventListener("resize", onResize, false);
    </script>
  </body>
</html>

開発環境を含む実践的なコード

開発環境の設定ファイルやスクリプトなど全て含んだコードは以下のGitHubに置いています。

上記のコードのように一つのHTMLファイルにまとめていないので、ディレクトリ構造がやや複雑になっていますが、やっていること自体は、上のものと同じです。



参考:

以上、ありがとうございました。
このエントリーをはてなブックマークに追加