Blenderからthree.jsにモデルをインポートする際に困ったこととそれに対する対処法、Tips

# 1. 最初に
　Blenderで作成したモデルをThree.jsで使用する際に思い通りにいかなかったことが多かったので、対処法やTipsをまとめたいと思います。

:::note info
Reactを使用しているのでThree.jsのラッパーライブラリである"react-three-fiber（以下、r3f）"を使用しています。
基本的にプロパティ名などは生のThree.jsと同じなので、r3fを使っていない方は適宜読み替えてください。
:::

# 2. そもそもどうやってモデルをインポートするのか
　r3fを使っている場合はGLTFファイルをJSXファイルに変換してくれるgltfjsxを使うと簡単です。

https://www.npmjs.com/package/@react-three/gltfjsx

　Three.jsではGLTFLoaderを使うか、後述するDraco圧縮を行った場合はDracoLoaderを使いましょう。

# 3. マテリアル表示の齟齬
　一番最初にぶつかるであろうマテリアルの問題についてです。
　Three.js側で作成したマテリアルとBlenderで設定したマテリアルではデフォルトの設定が異なっている部分もあるため、Three.jsで読み込んだ際に表示が異なる場合があります。

## 3-1. Blenderで設定したマテリアルのアルファ値、放射などが反映されない
* アルファ
　Blenderでマテリアルのアルファを1以下にするだけではThree.jsで透過されません。これを透過させるにはビューポート表示のブレンドモードをアルファブレンドにする必要があります。
![スクリーンショット (71).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/f747e399-462e-0835-1184-99a098cab52d.png)

以下は実際にThree.jsに読み込んだものの画像です。いずれのマテリアルもアルファ値は0.2程度で、左から「ブレンドモードが不透明」、「ブレンドモードがアルファブレンド」、「Three.jsで作成した透過マテリアル」です。
![スクリーンショット (72).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/dedcd97a-4393-9c3d-f0d8-072d807aa756.png)

* 放射
　放射マテリアルについてはBlenderでは自身が発光すると同時に周囲に光を投げるように描画されますが、Three.jsで読み込むと周囲に光を発してくれません。これを表現するにはshaderを使って自作する必要があるようですが詳しくないので実装方法などはわかりません。

:::note info 
Tips: オブジェクトが静止しておりダイナミックな動きが必要ないのであれば、周囲のオブジェクトにテクスチャベイクを施すことで周囲に光を投げているように見せることはできます。
:::

## 3-2. 色味が違う
　こちらはBlenderとThree.jsで色の出力方法に違いがあるのが原因だと思います。また、Blenderではマテリアルビューの際にスタジオライトが設定されており、この有無もThree.jsで読み込んだ際の違いにつながっているのではと思っています。
![スクリーンショット (79).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/03977177-794e-84ec-4057-7bac828a585e.png)
### 対処法
* 色の出力法を変える
　昔の記事などを見るに、以前はrendererのgammaOutputというプロパティをtrueにすることで解決できていたようですが、こちらはThree.jsの過去のマイグレーションで削除されているので使えません。代わりにWebGLRenderer.outputEncodingを使ってとのことです。

https://github.com/mrdoob/three.js/wiki/Migration-Guide#r111--r112

WebGLRenderer.outputEncoding

https://threejs.org/docs/#api/en/renderers/WebGLRenderer.outputEncoding

* Blender同様にThree.jsのSceneにも環境光を設定する
　r3fのヘルパーであるdreiに環境光を設定するコンポーネントがあるのでそちらを利用します。金属質なマテリアルや透過が設定されているマテリアルなどはこちらを設定することでより質感が増します。また、physicallyCorrectLightsをtrueにするとより見栄えが良くなります。
![スクリーンショット (85).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/a91c20d3-904b-fb75-99c8-efa49a45efbd.png)
![スクリーンショット (82).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/4dcd29ab-ca51-5a27-47da-4deb7f1adb7d.png)
![スクリーンショット (83).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/c633ac22-b05d-5106-16ed-1ac83af40f4f.png)

　環境光に使うHDRIは以下のサイトからダウンロードできます。

https://polyhaven.com/

以上の2つをr3fで実装すると以下のようになります。
```jsx:canvas.js
import { Canvas } from "@react-three/fiber";
import * as THREE from "three"
import { Environment } from "@react-three/drei";

export default function Scene() {
  return (
    <Canvas
      // rendererの設定
      onCreated={({gl}) => {
        gl.physicallyCorrectLights = true
        gl.outputEncoding = THREE.sRGBEncoding
      }}
    >
      // 環境光
      <Environment file={"/~~~.hdr"} />
    </Canvas>
  )
}
```
この辺りはライティングの強度やHDRIの種類などパターンが多すぎるので自身でいじって調整してみてください。

:::note info
Tips: テクスチャベイクを使う方法もありますが、モデルが複雑だと手間が多くファイルサイズも大きくなるのでお勧めしません。
:::


## 3-3．ほかのオブジェクトに包まれているオブジェクトが描画されない
　水槽などをモデリングする際に透明なガラス容器の中に透明な水のオブジェクトを配置することがあります。こういったオブジェクトは何も設定をしていないと中身のオブジェクトが表示されません。
![スクリーンショット (74).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/31d3a791-1753-b83d-0983-59d9ac5278a7.png)
### 対処法
　ガラスオブジェクトのマテリアルプロパティ「depthWrite」をfalseにします。
![スクリーンショット (75).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/7fce4af9-5325-5c39-61b2-c3ea0b33323a.png)


# 4. 影の描写
　Sceneの奥行きやモデルの存在感を出すためにも影の描画は必須です。しかし影に関する処理は重くなりがちなので、Three.jsでは影に関する設定がデフォルトで軒並みオフになっています。それらを自分で設定していく過程で思い通りにいかないこともままあります。

## 4-1. マテリアルが受ける影の様子がおかしい
![スクリーンショット (77).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/b81c1fbc-9b18-3ab8-66ff-eb26dc71462c.png)
　SceneにLightを設定してオブジェクトが影を受けるようにした際に、Blenderから持ってきたオブジェクトの受ける影がおかしくなるときがあります。この原因はBlnderとThree.jsで「マテリアルの表裏のどちらを描画するか」に関する設定が異なるためです。Blenderではデフォルトで両面とも描画する設定になっており、Three.js側で作成したマテリアルではできるだけ処理を軽くするためにデフォルトで表面のみ描画する設定となっています。プロパティはMaterial.sideです。

https://threejs.org/docs/#api/en/materials/Material.side

　マテリアルの両面が描画されることによって上の画像のような影になってしまうわけです。

### 対処法
　Blenderで作成したマテリアルで裏面を非表示にする設定をしてあげます。下の画像のように各マテリアルの設定から「裏面を非表示」にチェックを入れるだけです。
![スクリーンショット (76).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/5e082df3-6cc6-9c12-a594-1f457592a76d.png)


:::note info
Tips: マテリアルの数が多いと面倒な作業なので私はお気に入りツールに入れています。
:::

## 4-2. そもそも影が描画されない
　影を描画するにはScene、Light、meshの3つに影に関する設定をしないといけません。r3fでは以下のようになります。

```jsx:canvas.js
import { Box, Plane } from "@react-three/drei"; //　r3fのヘルパー
import { Canvas } from "@react-three/fiber";

export default function Scene() {
  return (
    <Canvas 
      shadows // Scene内での影の描写を許可
    >
      <ambientLight />
      <pointLight 
        castShadow // 影を投げかける設定
        shadow-mapSize={[1024, 1024]} // 影の解像度　デフォ[512、512]
        position={[0, 5, 0]} 
      />
      
      <Plane 
        args={[30, 30]} 
        rotation={[-Math.PI / 2, 0, 0]}
        position={[0, -1, 0]}
        receiveShadow // 影を受ける設定
      >
        <meshStandardMaterial color={"green"} />
      </Plane>
      
      <Box 
        castShadow // 影を投げかける設定
      > 
        <meshStandardMaterial color={"darkorange"} />
      </Box>
    </Canvas>
  )
}
```
![スクリーンショット (78).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/b98a8a0e-5b3e-137c-0b41-c62f8dbd447b.png)

:::note warn
Tips: 上記で指定している影の解像度は2のべき乗にする必要があります。また私の場合、これを2048以上にすると処理が重すぎるのかスマホでのページ読み込みでエラーを吐いたので1024にしておくことをお勧めします。
:::

# 5. モデルのファイルサイズが大きすぎる
 　モデルのポリゴン数が増えてくるとモデルのファイルサイズがどうしても大きくなり、ロードに時間がかかるようになります。特にモバイルデバイスで読み込む際にロード時間の伸びが顕著です。
### 対処法
私の知る限り以下の2つが挙げられます。
* モデルのポリゴン数を減らす。
    　頑張ってポリゴン数を減らしましょう。具体的には「メッシュの非常に近接した頂点をマージする」、「視覚に入らない余分な面などを削除する」、「デシメートモディファイアを使う」、「ベベルの程度を控えめにする」、「ハイポリのモデルにはリトポロジーを行う」などがあります。いずれもBlender側での操作です。特にベベルは一気にポリゴン数が増加するので気を付けましょう。

* Draco圧縮を用いる。
　こちらは2つ方法があります。一番簡単なのはBlenderからエクスポートする際の設定で圧縮のチェックを入れる方法です。圧縮のパラメータが色々ありますが、よくわからないのでデフォルトの値で圧縮しています。
![スクリーンショット (70).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/d56ca260-6662-e3b2-e9ca-c9f0e2cdf940.png)
　ほかにgltf-pipelineを使う方法がありますが、こちらは同様の記事があるため具体的な方法は割愛します。
　3D制作でBlenderを使っているのであればDraco圧縮は前者の方法でよいと思います。個人的にもファイルサイズが減少するだけで、圧縮する前と後でジオメトリやマテリアルに変化は感じられないので気兼ねなく圧縮しています。以下にファイルサイズを比較した画像を載せておきます。
![スクリーンショット (73).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852659/96b14ac8-196e-96bc-58a6-afd145810c92.png)

# 6. 参考になるサイト
* https://github.com/pmndrs/drei
    * r3のヘルパーであるdreiのgithubページです。使用例などが豊富で大変参考になります。
* https://codesandbox.io/search
    * codesandboxで色々探してみるのも面白いです。

# 7. 最後に
　以上で内容はすべてになります。ほかにも何かあれば追記していきます。
　ポートフォリオサイトがあるので興味がある方はのぞいてみてください。

https://portfolio-delta-liart-27.vercel.app/

この記事が少しでも役に立てば幸いです。