ReactThreeFiberで3Dシーンを構築

この記事は[ファンタラクティブ2024年アドベントカレンダー](https://qiita.com/advent-calendar/2024/funteractive-inc)12月13日の記事です

# React Three Fiberで3Dシーンを構築する

React Three Fiberを使用してインタラクティブな3Dシーンを作成するのは、意外とシンプルで楽しい作業です。この記事では、@react-three/fiberと@react-three/dreiなどを使って3Dシーンを構築していきます。

# 完成する3Dシーンの概要

今回作成するシーンでは、以下の要素を含みます：

- カプセル型の3Dオブジェクトがランダムに浮遊
- 動的に動く背景
- ガラスや金属のようなマテリアルを使用した3D文字
- カメラの動きがマウス入力に反応

## プレビュー：

https://codesandbox.io/embed/f2xsrk?view=preview&module=%2Fsrc%2FCanvasContent.tsx

## コード：

https://codesandbox.io/embed/f2xsrk?view=editor&module=%2Fsrc%2FCanvasContent.tsx

# 使用しているライブラリ

今回使用しているライブラリの役割や特徴を簡単にまとめてみました。

## Three.js

Three.jsは、WebGLを簡単に扱えるようにするライブラリで、3Dオブジェクトの描画やアニメーションを実現します。本記事では、マテリアル、ジオメトリ、カメラの設定などの基盤となる部分に使用しています。

https://threejs.org/

### 役割：

3Dグラフィックスを扱うためのJavaScriptライブラリ

### @react-three/fiber

React Three Fiberは、Three.jsをReact環境で使えるようにするラッパーです。Three.jsの複雑な部分を隠し、Reactのコンポーネントスタイルでシーンやオブジェクトを定義できます。

https://r3f.docs.pmnd.rs/getting-started/introduction

### 役割：

ReactコンポーネントでThree.jsを扱うためのラッパーライブラリ

### 主な特徴：

- Reactの状態管理と組み合わせやすい
- シンプルなAPIでThree.jsのほぼすべての機能にアクセス可能

## @react-three/drei

@react-three/dreiは、React Three Fiberをさらに便利にするためのヘルパーコンポーネント集です。背景の生成、オブジェクトの配置補助、カメラコントロールなどの機能が含まれています。

https://drei.docs.pmnd.rs/getting-started/introduction

### 役割：

React Three Fiber向けの便利なコンポーネント集

### 本記事で使用した主なコンポーネント：

- Float: オブジェクトに自然な浮遊アニメーションを追加
- Text3D: 立体的なテキストを簡単に作成
- Center: 子要素を自動的に中央に配置

## Lamina

Laminaは、カスタムシェーダーやマテリアルを簡単に定義できるライブラリです。グラデーション、ノイズ、透明感など、複雑なマテリアルの表現をReactで直感的に構築できます。

https://github.com/pmndrs/lamina

### 役割：

シェーダーマテリアルを簡単に作成するライブラリ

### 本記事で使用した主なレイヤー：

- Depth: 距離に基づいた色のグラデーションを追加
- Noise: ノイズを加えて質感を向上

## canvas-sketch-util

canvas-sketch-utilは、アート生成やプロシージャルデザイン向けに作られたツールで、ランダム値やノイズ生成を簡単に実現します。

https://github.com/mattdesl/canvas-sketch-util

### 役割：

ランダム性を追加するためのユーティリティライブラリ

### 本記事で使用した関数：

- Random.range(min, max): 指定範囲内のランダムな数値を生成
- Random.pick(array): 配列からランダムな要素を選択

# カメラの動き

useFrameを使ってカメラの動きをマウス入力に反応させます。この機能により、ユーザーがインタラクティブにシーンを探索できるようになります。

```
import * as THREE from "three"; 

...省略

const Rig = ({ v = new THREE.Vector3() }) => {
  return useFrame((state) => {
    state.camera.position.lerp(v.set(state.mouse.x, state.mouse.y, 14), 0.01);
  });
};
```

# シーンの背景

背景は@react-three/dreiのLayerMaterialを使って、深さ感やノイズを追加しています。色のグラデーションやノイズの影響で、奥行き感のある雰囲気を演出できます。

```
import { LayerMaterial, Depth, Fresnel, Noise, Color } from "lamina";

...省略

const Bg = () => {
  const num = 100; // 背景サイズ
  return (
    <mesh scale={1}>
      <boxGeometry args={[num, num, -50]} />
      <LayerMaterial side={THREE.FrontSide}>
        <Depth
          colorA="#FFDFD6"
          colorB="#522258"
          near={num / 6}
          far={num}
        />
        <Noise scale={1000} alpha={0.5} />
      </LayerMaterial>
    </mesh>
  );
};
```

# 浮遊するカプセル

ランダムな位置に配置されるカプセル型オブジェクトを生成します。@react-three/dreiのFloatを使うことで、自然な浮遊アニメーションを簡単に追加できます。今回、<Capsule>使いましたがこれ以外にも、さまざまな形状のオブジェクトを生成し、同様にランダム配置や浮遊アニメーションを付加することができます。

- 球体：<Sphere>
- 立方体：<Box>
- ドーナツ型：<Torus>
- カスタムジオメトリ：coneGeometryを使ってピラミッド型オブジェクトを作成

色に関してはこちらのサイトから選択しました：https://colorhunt.co/

```
import { Float, Text3D, Center, Capsule } from "@react-three/drei";

...省略

const Capsules = () => {
  const size = 2.5;
  const colors = ["#AB4459", "#C23373", "#F29F58", "#F6635C", "#EB5B00"];

  const blocks = new Array(40).fill(0).map((_, index) => ({
    id: index,
    position: [
      Random.range(-size * 3, size * 3),
      Random.range(-size, size),
      Random.range(-size, size),
    ],
    size: Random.range(0.1875, 0.375) * size,
    color: Random.pick(colors),
  }));

  return (
    <>
      {blocks.map((block) => (
        <Float
          key={block.id}
          position={block.position}
          scale={block.size}
          floatIntensity={2}
        >
          <Capsule args={[0.3, 0.9, 4, 8]} castShadow>
            <meshPhongMaterial color={block.color} />
          </Capsule>
        </Float>
      ))}
    </>
  );
};
```

# 3Dテキスト

## フォントをJSONに変換

フォントをJSON形式に変換する手順を詳しく説明します。この方法を使うと、@react-three/dreiのText3Dで使用できる3Dフォントを簡単に作成できます。
フリーのフォントをダウンロードしフォント変換ツールを使います：https://gero3.github.io/facetype.js/
変換が完了すると、自動的にText3Dに利用できるJSONファイルが生成されます。

## マテリアルを適用させる

Text3Dを利用して、立体的なテキストを追加します。このテキストにはガラスのようなマテリアルを適用し、質感をリアルに表現しています。今回はガラスっぽい感じのマテリアルを使いましたが、コード上金属っぽいマテリアルとゴムっぽいマテリアルも用意してみました。

```
const Caption = ({ children }: { children: ReactNode }) => {
    const { width } = useThree((state) => state.viewport);
    const color = "#B692C2";
    const glassMaterial = new THREE.MeshPhysicalMaterial({
      thickness: 20,
      clearcoat: 1,
      clearcoatRoughness: 1,
      transmission: 0.5,
      roughness: 0.1,
      ior: 1.5,
      color,
    });
    return (
      <Text3D
        font="/Bubbles_Regular.json"
        size={width / 8}
        height={0.5}
        bevelEnabled
        bevelSize={0.05}
        bevelThickness={0.1}
        material={glassMaterial}
      >
        {children}
      </Text3D>
    );
  };
```


### 金属っぽいマテリアル:

```
    const metalMaterial = new THREE.MeshPhysicalMaterial({
      thickness: 20,
      clearcoat: 1,
      clearcoatRoughness: 1,
      transmission: 0.3,
      roughness: 0.5,
      metalness: 1,
      color,
    });
```


### ゴムっぽいマテリアル:

```
    const plasticsMaterial = new THREE.MeshPhysicalMaterial({
      thickness: 20,
      clearcoat: 1,
      clearcoatRoughness: 1,
      roughness: 1,
      metalness: 0,
      color,
    });
```


# 最後に

React Three Fiberを使うことで、Three.jsの複雑さを隠しながら、直感的に3Dコンテンツを構築できます。ぜひオリジナルのシーンを作ってみてください！
明日の記事は⭐️ファンタラクティブのエンジニアリングマネージャーyagikoさんです〜! ⭐️