【Three.js】Blenderで作った3Dモデルを動かす

2021年10月30日JavaScript,Three.js

美女とは…。

はじめに

Blenderで人間の3Dモデルを作ったんです。それをThree.jsで読み込んだんです。そしたらやっぱり動かしたいじゃないですか。

今回はそんな感じのことをやっていきます。

今回Blenderで作ったもの

美しい女性。五十嵐三姉妹の長女という設定です。

右上のところに各パーツの名前が載っていますね。五十嵐さんは4つのパーツでできています。

まず顔。Faceという名前を付けたオブジェクトです。見づらいかもしれませんがオレンジ色の線で囲まれている部分です。

次にHair

そしてLeftEyeと、

RightEye

3Dモデルをエクスポートする

五十嵐さんをThree.jsで読み込みます。まずはglTF形式でエクスポートしましょう。

【ファイル】→【エクスポート】→【glTF2.0】と選択していきます。

するとこんな画面↑が出てきて、なんか色々設定できそうですが、よく分からないのでそのままエクスポートします。

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

//*****************************************
// 五十嵐さんを作成する
//*****************************************
function createIgarashi() {
  
  const loader = new GLTFLoader();
  const igarashiURL = "./gltf/human/baseball/igarashi.glb";
  
  loader.load(igarashiURL, function(gltf) {
    const igarashi = gltf.scene;
    scene.add(igarashi);
  });

}

先程エクスポートしたファイルをGLTFLoaderクラスのloadメソッドを使って読み込み完了。

読み込んだ3Dモデルをsceneに追加してブラウザで表示します。ここからが本題ですね。この五十嵐さんを動かしていきましょう。

目玉を動かしてみる

まずは目玉だけを動かします。先程の五十嵐さん作成関数に少し手を加えます。

//*****************************************
// 五十嵐さんを作成する
//*****************************************
function createIgarashi() {
  
  const loader = new GLTFLoader();
  const igarashiURL = "./gltf/human/baseball/igarashi.glb";
  
  loader.load(igarashiURL, function(gltf) {
    const igarashi = gltf.scene;

    for (let i = 0; i < igarashi.children.length; i++) {
      
      if (igarashi.children[i].name == "LeftEye") {
        igarashiLeftEye = igarashi.children[i];
      }

      if (igarashi.children[i].name == "RightEye") {
        igarashiRightEye = igarashi.children[i];
      }

    }

    scene.add(igarashi);

  });

}

12~22行目を追加しました。igarashi.childrenは五十嵐さんの頭部を構成するパーツ、すなわち「Face」「Hair」「LeftEye」「RightEye」です。その内の「LeftEye」と「RightEye」を探して変数に入れておきます。

そして目玉をマウスカーソルの方向に向かせる処理がコレです↓。

//*****************************************
// 五十嵐さんを動かす
//*****************************************
function moveIgarashi(e) {

  // ディスプレイ上でのマウスの座標(-1~1に変換)
  const dispX = (e.clientX / window.innerWidth) * 2 - 1;
  const dispY = -(e.clientY / window.innerHeight) * 2 + 1;

  // Z方向の+30の位置における3D空間の高さと幅
  const heightOnOrigin = (Math.tan(((fov * Math.PI / 180) / 2)) * (30) * 2)
  const widthOnOrigin = heightOnOrigin * aspRatio

  // 五十嵐LeftEyeの向きを変える
  igarashiLeftEye.lookAt(new THREE.Vector3(dispX * widthOnOrigin / 2, dispY * heightOnOrigin / 2, 30));

  // 五十嵐RightEyeの向きを変える
  igarashiRightEye.lookAt(new THREE.Vector3(dispX * widthOnOrigin / 2, dispY * heightOnOrigin / 2, 30));

}

window.addEventListener("mousemove", moveIgarashi)

マウスの座標をなんやかんやして五十嵐さん動かし関数を作ります。なんやかんやの詳細はコチラをどうぞ。五十嵐さん動かし関数をmousemoveイベントと紐づけてあげる事で、目玉が常にマウスカーソルの方向を向くようになります。

そしてできたのがコレ↓。

ちゃんと動いていますね。今度は顔全体を動かしてみましょう。

顔全体を動かす

顔全体を動かすので、顔を構成する4つのパーツすべてを変数に入れてあげましょう。

五十嵐さん作成関数を少しイジります。

//*****************************************
// 五十嵐さんを作成する
//*****************************************
function createIgarashi() {
  
  const loader = new GLTFLoader();
  const igarashiURL = "./gltf/human/baseball/igarashi.glb";
  
  loader.load(igarashiURL, function(gltf) {
    const igarashi = gltf.scene;

    for (let i = 0; i < igarashi.children.length; i++) {

      if (igarashi.children[i].name == "Face") {
        igarashiFace = igarashi.children[i];
      }

      if (igarashi.children[i].name == "Hair") {
        igarashiHair = igarashi.children[i];
      }

      if (igarashi.children[i].name == "LeftEye") {
        igarashiLeftEye = igarashi.children[i];
      }

      if (igarashi.children[i].name == "RightEye") {
        igarashiRightEye = igarashi.children[i];
      }

    }
    
    scene.add(igarashi);

  });

}

14行目から20行目を追加しました。「Face」と「Hair」を取得しています。

次に、五十嵐さん動かし関数も変更します。

//*****************************************
// 五十嵐さんを動かす
//*****************************************
function moveIgarashi(e) {

  // ディスプレイ上でのマウスの座標(-1~1に変換)
  const dispX = (e.clientX / window.innerWidth) * 2 - 1;
  const dispY = -(e.clientY / window.innerHeight) * 2 + 1;

  // Z方向の+30の位置における3D空間の高さと幅
  const heightOnOrigin = (Math.tan(((fov * Math.PI / 180) / 2)) * (30) * 2)
  const widthOnOrigin = heightOnOrigin * aspRatio

  // 五十嵐Faceの向きを変える
  igarashiFace.lookAt(new THREE.Vector3(dispX * widthOnOrigin / 2, dispY * heightOnOrigin / 2, 30));

  // 五十嵐Hairの向きを変える
  igarashiHair.lookAt(new THREE.Vector3(dispX * widthOnOrigin / 2, dispY * heightOnOrigin / 2, 30));

  // 五十嵐LeftEyeの向きを変える
  igarashiLeftEye.lookAt(new THREE.Vector3(dispX * widthOnOrigin / 2, dispY * heightOnOrigin / 2, 30));

  // 五十嵐RightEyeの向きを変える
  igarashiRightEye.lookAt(new THREE.Vector3(dispX * widthOnOrigin / 2, dispY * heightOnOrigin / 2, 30));

}

window.addEventListener("mousemove", moveIgarashi)

14行目から18行目を追加。顔面と髪の毛も動くようにしています。

これでできたのがコレ↓。

なんか変ですよね。すべてのパーツが動いてはいるんですけど、目玉の位置がズレちゃってます。

自分の顔を触れば分かりますけど、目玉って顔にくっついてますよね。だから顔が動けば目玉の位置も動かないといけないのに、これはそうなってない。その場で回転しているだけだから目玉がズレてしまっているのです。

顔が動いたら目玉の位置も動かさないといけないのです。どうすればいいのでしょうか?

Blender側で全部のパーツをくっつけちゃう

簡単。全部くっつけちゃえばいいじゃん。というワケでくっつけます。

4つのパーツを全部選択→【オブジェクト】→【統合】をクリック。

4つのパーツを選択する時にベースにしたいオブジェクトを最後に選んでください。今回の場合だと「Face」です。

こうすると4つのパーツがくっつきます。右上の所を見てください。「Face」だけになっているのが分かると思います。

で、これをエクスポートした後にThree.jsで読み込みます。変数に入れたり動かしたりするオブジェクトは「Face」だけになりますので、そこだけ注意です。

こうなります↓。

できたできた。でも全部のパーツをくっつけてしまったので、目玉だけを個別に動かすことはできなくなります。何か方法があるかもしれないので、また気が向いた時にでも挑戦してみます。

あとがき

自分で作った3Dモデルが動いているってスゴイですね。ほんの少し動いただけでもとても楽しいです。

Blenderでもっと色々作れるようになりたい。今回は以上です。ありがとうございました。

おしゃれ度

★★☆☆☆

Posted by ナカタ