MENU

【Three.js】Blenderで作成した3DモデルをWebページで色・表示/非表示を変更する方法

こんにちは、もしくはこんばんは。シロです!

絶賛サボりまくっていました。

この記事では、Three.jsで3Dモデルの色変更やオブジェクトの一部を表示/非表示を切り替える方法について、ご紹介します!

Three.jsを使ってWebページに3Dモデルを表示する方法については、以下の記事をご覧ください!
こちらのページに記載のコードを一部変更して、ご紹介しています。

目次

できるようになること

この記事を読むと、Webページに表示した3Dモデルの色変更や、オブジェクトの一部の表示/非表示を切り替えられるようになります。

デモページを用意しました。マウス操作もできるようになっているので、ぜひ3Dモデルをぐるぐる操作してみてください。

前提条件

  • 3Dモデルをもっていること
  • Blenderの基本的な操作ができること
  • Javascript/CSSの基礎知識があること

ステップ1:3Dモデルの準備、ステップ2:Three.jsのインポート

以下の記事を御覧ください。

ステップ3:3Dモデルの表示

Webページの<body>にGLTF形式の3Dモデルを表示するスクリプトを記載します。以下はスクリプトの全文です。

  <body>
    <script type="module">
        // 1. Three.jsライブラリの読み込み
        import * as THREE from 'three';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
        import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
        import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';

        // 2. シーンを作成する
        const scene = new THREE.Scene();

        // 3. カメラを作成する
        const camera = new THREE.PerspectiveCamera(
            75, // 視野角
            window.innerWidth / window.innerHeight, // アスペクト比
            0.1, // ニアクリップ面
            1000 // ファークリップ面
        );
        camera.position.z = 5; // カメラの位置を設定する

        // 4. レンダラーを作成する
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        const controls = new OrbitControls(camera, renderer.domElement);
        controls.zoomSpeed = 0.5;
        controls.update(); // 初期化時に一度だけ呼び出す

        // AmbientLightを追加する
        const ambientLight = new THREE.AmbientLight(0xffffff, 1);
        scene.add(ambientLight);

        // 5. GLTFLoaderを作成する
        const loader = new GLTFLoader();
        
        const dracoLoader = new DRACOLoader(); // DRACOLoaderのインスタンスを作成
        dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.1/');
        loader.setDRACOLoader(dracoLoader); // GLTFLoaderにDRACOLoaderのインスタンスを提供

        // 6. 3Dモデルのロード&描画
        let model = null;
        loader.load(
            "path/to/model.gltf",
            function (gltf) {
                scene.add(gltf.scene);
                model = gltf.scene;     // 表示中のモデルを保持しておく
            },
            undefined,
            function (error) {
                console.error(error);
            }
        );

        function animate() {
            requestAnimationFrame(animate);
            renderer.render(scene, camera);
        }
        animate();

        // 色変更関数
        let original_color = [];
        window.changeColor = function(materialName, color) {
            model.traverse((child) => {
                if (child.isMesh && child.name === materialName) {
                    const material = child.material;
                    // オリジナルのマテリアルデータを保持しておく
                    if (original_color.indexOf(materialName) < 0) {
                        original_color[materialName] = child.material.clone();
                    }
                    material.color.set(color);
                }
            });
        }

        // 色リセット関数
        window.resetColor = function(materialName) {
            model.traverse((child) => {
                if (child.isMesh && child.name === materialName) {
                    const material = child.material;
                    if (original_color[materialName]) {
                        material.color.copy(original_color[materialName].color);
                    }
                }
            });
        }

        // オブジェクトの表示・非表示
        window.visibleObject = function(materialName, direct) {
            model.traverse((child) => {
                if (child.isMesh && child.name === materialName) {
                    child.visible = direct;    // True:表示、False:非表示
                }
            });
        }
    </script>

    <div>
      <button onclick="changeColor('body', 0xFF0000)">赤</button>
      <button onclick="changeColor('body', 0x00FF00)">緑</button>
      <button onclick="changeColor('body', 0x0000FF)">青</button>
      <button onclick="resetColor('body')">もとに戻す</button>

      <button onclick="visibleObject('tail', false)">尻尾を隠す</button>
      <button onclick="visibleObject('tail', true)">尻尾を表示する</button>
    </div>
  </body>

1〜5. 前述の記事と同様

前述の記事と同じなので省略します。

6. 3Dモデルのロード&描画

        let model = null;
        loader.load(
            "path/to/model.gltf",
            function (gltf) {
                scene.add(gltf.scene);
                model = gltf.scene;     // 表示中のモデルを保持しておく
            },
            undefined,
            function (error) {
                console.error(error);
            }
        );

この時点でロードしたモデルデータをmodelに保持しておきます。

7. 各関数

        // 色変更関数
        let original_color = [];
        window.changeColor = function(materialName, color) {
            model.traverse((child) => {
                if (child.isMesh && child.name === materialName) {
                    const material = child.material;
                    // オリジナルのマテリアルデータを保持しておく
                    if (original_color.indexOf(materialName) < 0) {
                        original_color[materialName] = child.material.clone();
                    }
                    material.color.set(color);
                }
            });
        }

        // 色リセット関数
        window.resetColor = function(materialName) {
            model.traverse((child) => {
                if (child.isMesh && child.name === materialName) {
                    const material = child.material;
                    if (original_color[materialName]) {
                        material.color.copy(original_color[materialName].color);
                    }
                }
            });
        }

        // オブジェクトの表示・非表示
        window.visibleObject = function(materialName, direct) {
            model.traverse((child) => {
                if (child.isMesh && child.name === materialName) {
                    child.visible = direct;    // True:表示、False:非表示
                }
            });
        }

このコードは、色の変更・リセット、オブジェクトの表示・非表示を切り替えるためのものです。

window.changeColor()関数は対象となるマテリアルの名前と、色(16進数)を引数としています。
またこのとき、後で元に戻せるようにオリジナルのマテリアルデータを保持しておきます。

window.resetColor()関数は対象となるマテリアルの名前を引数とし、対象マテリアルのカラーをオリジナルの色に戻しています。

window.visibleObject()関数は対象となるマテリアルの名前と、表示・非表示を引数とし、対象マテリアルの表示プロパティvisibleを変更します。

以下のコードで上記JS関数を呼び出すボタン要素を追加しています。

    <div>
      <button onclick="changeColor('body', 0xFF0000)">赤</button>
      <button onclick="changeColor('body', 0x00FF00)">緑</button>
      <button onclick="changeColor('body', 0x0000FF)">青</button>
      <button onclick="resetColor('body')">もとに戻す</button>

      <button onclick="visibleObject('tail', false)">尻尾を隠す</button>
      <button onclick="visibleObject('tail', true)">尻尾を表示する</button>
    </div>

まとめ

この記事では、Thee.jsを使ってWebページ上に表示した3Dモデルの色や表示状態を変更する方法について解説しました。

Three.jsにはJavaScriptで3Dモデルを操作する方法が多数用意されています。

ぜひこの記事を参考にして、より魅力的なコンテンツを自分のウェブサイトやブログに追加してみてください!

以上、【Three.js】Blenderで作成した3DモデルをWebページで色・表示/非表示を変更する方法についての紹介でした。

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!

コメント

コメント一覧 (5件)

  • シロさま

    いつもありがとうございます。
    コードを拝見しました。まだblenderを始めたばかりで、blenderから勉強段階ではあるのですが、
    《関数・引数》
     7.各関数(27行目)

    《利用する場合》
    各コード呼び出し(7行目)で設定をしているという事は、
    blenderでマテリアル名をそれぞれで設定することで対応が出来るという認識をしましたが
    合っていますか?

    blender側でしっぽに当たる部分にtailと名前を付けることでjsで動くという事ですよね?

    という事は例えば
    帽子1、帽子2、帽子3と作成した場合
    マテリアル名hat1、hat2、hat3
    としたうえで
    (‘tail’, false) → (‘hat1’, true)
    (‘tail’, false) → (‘hat2’, true)
    とボタンによって設定することで出来ると思いました。
    ただhat1→hat2としたときはhat1を非表示にする処理が必要になってくるとは思いますが・・・

    という認識でしょうか?

    • FJさま

      ご認識であっているかと思います。
      Blenderでのマテリアルの設定については、以下の記事でまとめていますのでご覧ください。
      https://mawasu-blog.com/modelviewer_change_material_color/

      記事のタイトルはmodel-viewerというAPIについてですが、model-viewerの中身はThree.jsなので大まかには同じです。

      オブジェクトの切りかえをボタンでするなら、例えば以下の方法が考えられます。(検証はしていないのでエラーがあったらすみません。。。)

              window.visibleObject = function(visibleMaterial, hiddenMaterials=[]) {
                  model.traverse((child) => {
                      if (child.isMesh && child.name === visibleMaterial) {
                          child.visible = true;    // True:表示、False:非表示
                      } else if (hiddenMaterials.include(child.name)) {
                          child.visible = false;
                      }
                  });
              }

      こちらは、表示したいオブジェクトのマテリアル名と、非表示にしたいオブジェクトのマテリアル名配列を引数にしています。
      こちらはもうJSの書き方しだいですね。
      ざっと考えたので、もっといい方法もあるかもしれません。

      お時間があるようでしたら、もうちょっと汎用的に使えるコードを考えてみたいと思います。

      • シロさま

        いつも詳細ありがとうございます。blenderのページも参考にします!

        ちなみにですが
        赤・青をボタンを押してjsが動作するようになっていますが
        ボタンでは無くてドロップダウンリスト(select)でも同じことは出来ますよね?

        button→inputになるという認識です。
        buttonだとどんどん増えていくなぁ・・・と思ってしまったので、、すみません(汗)

        • FJさま

          もちろんselectでも大丈夫です。
          selectの場合はonchangeイベントにchangeColor関数を記述してください。
          その場合、changeColor関数の引数にeventオブジェクトを設定し、eventオブジェクトから選択した値を取得する等の処理が必要です。

                  window.changeColor = function(event, materialName) {
                      let color = event.target.value;
                      model.traverse((child) => {
                          if (child.isMesh && child.name === materialName) {
                              const material = child.material;
                              // オリジナルのマテリアルデータを保持しておく
                              if (original_color.indexOf(materialName) < 0) {
                                  original_color[materialName] = child.material.clone();
                              }
                              material.color.set(new THREE.Color(color));
                          }
                      });
                  }
          • シロさま

            いつもお世話になっております。
            コードまでありがとうございます。いろいろ勉強していきたいと思います

            ちなみに今適当に部品を作ってマテリアル名を付けて、表示非表示テストをしているのですが、、動作してい無さそうでした。(いろいろ試してみます)

            よろしくお願いいたします。

目次