【React】react-three-fiberで自作の3Dモデルを手軽にWebサイトに表示する

# 背景
背景が変化するリッチなWebサイトをよく見かけます。
Three.jsでは3DモデルをWeebページ上に表示できるので、2Dでは表現できないようなリッチな背景をWebサイトに実装する事ができます。
ReactでもThree.jsのようなライブラリがないか調べたところ、three.js の React レンダラーである**react-three-fiber**がありました。
本記事では、react-three-fiberを使用し、Blenderで制作した3Dモデルをwebページ上に表示してみます。

### ◆完成イメージ図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/ba22c415-d4b4-6950-703a-b5ed5af46440.png)

- react-three-fiber

https://github.com/pmndrs/react-three-fiber

- drei

https://github.com/pmndrs/drei

# 前提
本記事では、React×TypeScript環境を使用します。
また、Blenderで作成した3Dモデルの準備が必要です。
Blenderで設定したブルームの表現については触れず、
3DモデルをWebサイト上に表示させるところに絞っています。


# 環境
- Blender：3.4
- Typescript：4.9.5
- React：18.2.0
- React-three-drei：9.88.11
- React-three-fiber：8.15.9

# 準備する3Dモデル
今回は桜をイメージした次の3Dモデルを表示させてみます。

![scherry_blossom_night.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/0371b94f-8d04-5856-7ba1-5109ac0a3398.png)

Blender側ではgltf形式でエクスポートしました。
設定は以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/ef67996f-d6dd-0229-5045-a2a9cccaa33f.png)

# 1. プロジェクトの作成
`--template typescript`を付けて、TypeScriptを使用するプロジェクトのひな形を作成します。
コマンドは以下の通りです。
```
npx create-react-app プロジェクト名 --template typescript
```
- 参考：https://create-react-app.dev/docs/adding-typescript/

# 2. ライブラリインストール
react-three-fiberとdreiを以下のコマンドでインストールします。
```
npm i three @react-three/fiber @react-three/drei
```
以下のコマンドでthreeの型定義モジュールをインストールします。
```
npm i -D @types/three
```
- 参考：https://docs.pmnd.rs/react-three-fiber/getting-started/installation
- 参考：https://www.npmjs.com/package/@types/three?activeTab=versions

# 3. フォルダ構成
一部分のみですがフォルダ構成は、以下の通りです。
assetsフォルダにBlenderでエクスポートした3Dモデル（sakura_tree.glb）を追加しています。
```
┬public
│ └assets
│　└sakura_tree.glb
└ src
 　├css
 　│ └index.css
 　├App.tsx
 　└index.tsx
```

# 4. GLTF形式の3Dモデルを読み込み
以下のコード部分では、`useGLTF`フックを使用してGLTF形式の3Dモデルを読み込み、
そのモデルの`scene`を作成しています。
`<primitive>`コンポーネントを使用して、作成したシーンを描画しています。
```react:App.tsx
function Model() {
  const { scene } = useGLTF("assets/sakura_tree.glb")
  return <primitive object={scene} />
}
```

# 5. キャンバスの描画
以下のコード部分では、`<Canvas> `コンポーネントを使用して3Dモデルをキャンバス内に描画しています。
キャンバスの背景色は`style={{ background: "#001122" }`でネイビー色を指定しています。
`camera`プロパティを使用してカメラの初期位置を指定しています。
```react:App.tsx
export default function App() {
  return (
    <>
      <Canvas camera={{ position: [2, 1, 2], near: 0.05 }}
       style={{ background: "#001122" }>
        {/* ～ */}
      </Canvas>
    </>
  )
}
```

# 6. 3Dモデルの描画
`<OrbitControls>`コンポーネントによりマウス操作でカメラの回転やズームを制御できます。
`autoRotate`プロパティが設定されているためカメラは水平方向に自動で回転します。
また、`<group>`コンポーネント内に`Model`コンポーネントを配置し3Dモデルを描画しています。
```react:App.tsx
export default function App() {
  return (
    <>
      <Canvas camera={{ position: [2, 1, 2], near: 0.05 }}
       style={{ background: "#001122" }>
        {/* 3Dモデルの表示 */}
        <group>
          <Model />
        </group>
        {/* 自動横回転 */}
        <OrbitControls autoRotate />
        {/* フレームレート情報 */}
        <Stats />
      </Canvas>
    </>
  )
}
```

:::note info
補足
<Stats> `Stats`コンポーネントを使用する事で、画面にフレーム レート情報を表示できます。
必要ない場合は削除してください。
- 参考：https://sbcode.net/react-three-fiber/stats/
:::

### ・App.tsxのコード全体
以上を踏まえたApp.tsx全体のコードです。
```react:App.tsx
import { Canvas } from '@react-three/fiber'
import { Stats, OrbitControls, useGLTF } from '@react-three/drei'


function Model() {
  const { scene } = useGLTF("assets/sakura_tree.glb")
  return <primitive object={scene} />
}

export default function App() {

  return (
    <>
      <Canvas camera={{ position: [2, 1, 2], near: 0.05 }}>
        <group>
          <Model />
        </group>
        <OrbitControls autoRotate />
        <Stats />
      </Canvas>
    </>
  )
}
```

### ・index.tsxのコード全体
`root.render`メソッドを使用してidが`root`のDOM要素にレンダリングします。

```typescript
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import "./css/index.css";

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
```

### ・index.cssのコード全体
ページ全体にコンポーネントを描画します。
```css
body {
  margin: 0;
  overflow: hidden;
}

#root {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
```
# 7. 描画結果
ここまでできたら以下のコマンドで、次のコマンドで起動します。

```
npm start
```
実行すると以下画像のように3Dモデルをwebサイト上に表示出来ていました。
しかし、Blenderでの見え方と異なっており、3Dモデルに影がなく花びら部分がのっぺりとしたボールのようになっています。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/9d5e6442-1447-8310-2932-32a5c6bef2f0.png)

# 8. 改善：ライトの設置
ライトを設置していなかったため、影が投影されていなかった可能性がありました。
以下のコードをApp.tsxファイルの`Canvas`部分に記載し、平行光源を追加します。
- directionalLight：平行光源
- position={[x, y, z]}：ライトの位置
- `castShadow`プロパティ：影の投影

```react
{/* ライト */}
<directionalLight position={[5, 5, 5]} castShadow />
```    

ライトを追加したApp.tsxは以下の通りです。
```react:App.tsx
  <Canvas camera={{ position: [2, 1, 2], near: 0.05 }} style={{ background: "#001122" }}>
    {/* 3Dモデルの表示 */}
    <group>
      <Model />
    </group>
    {/* 自動横回転 */}
    <OrbitControls autoRotate />
    {/* 統計情報 */}
    <Stats />
    {/* ライト */}
    <directionalLight position={[5, 5, 5]} castShadow />
  </Canvas>
```
平行光源を追加した事で、追加前に比べて花びら1枚１枚がよりはっきり見えるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/f46c58ad-bbe0-f93f-a977-d22c0ddb5954.png)

上記改善よりましにはなりましたが、
Blenderで3Dモデルに設定していたブルーム感がまだ反映されません。。
本記事では、react-three-fiberで3DモデルをWebサイト上に表示する事が目的でだったので、
一端ここまでとします。
ブルーム感を出す方法については、[**次の記事**](https://qiita.com/enumura1/items/c1dee84b87fc0f0692ad)に続きます。

- **次の記事**：react-three-fiberで表示した3Dモデルに発光感(ブルーム)を付ける

https://qiita.com/enumura1/items/c1dee84b87fc0f0692ad


 # 参考
 - https://create-react-app.dev/docs/adding-typescript/
 - https://docs.pmnd.rs/react-three-fiber/getting-started/installation
 - https://www.npmjs.com/package/@types/three?activeTab=versions
 - https://sbcode.net/react-three-fiber/stats/