@react-three/dreiを使って様々な3D表現を行う

[この記事](https://qiita.com/watataku8911/items/687b0609e0739cd1ad4f)の続きです。

`@react-three/drei(以下、dreiと呼称する。)は`@react-three/fiber(以下、r3fと呼称する。)`用の便利なヘルパー関数などを提供してくれるライブラリです。このライブラリとr3fと組み合わせることで、r3fだけでは表現できなかったことが表現できるようになります。

https://github.com/pmndrs/drei
## インストール
```console
$ npm install @react-three/drei
```
## パーティクル

https://codesandbox.io/embed/r3f-particle-xpc2z3?fontsize=14&hidenavigation=1&theme=dark

### Stars
```ts
import { Stars } from "@react-three/drei";
```
```tsx
<Stars
 radius={100} // 星の点滅(拡大)度合い
 depth={50} // 星の深さ
 count={5000} // 星の数
 factor={6} // 星の大きさ
 saturation={9} // 星の彩度
 speed={3} // 点滅のスピード
/>
```
[docs](https://github.com/pmndrs/drei#stars)

https://codesandbox.io/embed/r3f-particle-mm394l?fontsize=14&hidenavigation=1&theme=dark

### Points、Point
```ts
import { Points, Point } from "@react-three/drei";
```
```tsx
const pColors = ["red", "yellow", "orange", "blue", "green", "white"];

<Points limit={2000} range={1000}>
  {/* limit : パーティクルの数, range : パーティクルの描画範囲 */}
  <pointsMaterial vertexColors size={0.1} />
  {/* lengthの値はlimitの数に合わせる */}
  {Array.from({ length: 2000 }).map((_, i) => (
    <Point
      key={i}
      position={[
        MathUtils.randFloatSpread(10),
        MathUtils.randFloatSpread(10),
        MathUtils.randFloatSpread(10)
      ]}
      color={pColors[Math.floor(Math.random() * (pColors.length - 1))]}
    />
  ))}
</Points>
```
[docs](https://github.com/pmndrs/drei#points)
#### MathUtils
ここで、登場した`MathUtils`とはなんなのか紹介します。`MathUtils`はthree.js本体のあるものです。
```ts
import { MathUtils } from "three";
```
https://zenn.dev/ajitama_tkd/articles/fbc7bc6066bfbe
### OrbitControls
three.jsに存在していた手軽にカメラの動きを自動的に制御する **OrbitControls** がdreiに存在します。
```ts
import { OrbitControls } from "@react-three/drei";
```
```tsx
<OrbitControls />
```
この`<OrbitControls />`を使用する事によりマウスでオービット（周回軌道）できたり、マウスホイールでズームなどの操作ができます。

## 雲を表現する
https://codesandbox.io/embed/r3f-cloud-dchkcj?fontsize=14&hidenavigation=1&theme=dark

### Sky
この`<Sky />`を使用すると背景に「空」が描画されます。
```ts
import { Sky } from "@react-three/drei";
```
```tsx
<Sky />
```
[docs](https://github.com/pmndrs/drei#sky)
### Cloud
パーティクルベースの「雲」が描画されます。
```ts
import { Cloud } from "@react-three/drei";
```
```tsx
<Cloud
  position={[0, 0, 0]} // 雲のポジショニング
  opacity={0.5} // 不透明度
  speed={0.4} // 回転速度
  width={10} // 雲全体の幅
  depth={1.5} // Z方向の深さ
  segments={20} // パーティクルの数
/>
```
[docs](https://github.com/pmndrs/drei#cloud)
## 3Dモデルを表示する

https://codesandbox.io/embed/r3f-model-95v4wm?fontsize=14&hidenavigation=1&theme=dark

`blender`などで作成した3DモデルをWeb上に描画します。
描画する際には、`useGLTF()`関数を使用します。
```ts
import { useGLTF } from "@react-three/drei";
```
```ts
const reactLogo = useGLTF(
  "https://vazxmixjsiawhamofees.supabase.co/storage/v1/object/public/models/react-logo/model.gltf"
);
```
`useGLTF()`関数の引数に任意のパスを記述します。

※モデルは[ここ](https://market.pmnd.rs/model/react-logo)から持ってきました。
```tsx
<primitive object={reactLogo.scene} position={[0, -1.3, 0.3]} scale={[1.5, 1.5, 1.5]} />
// object : useGLTF()で読み込んだモデル.scene
// position : モデルのポジショニング
// scale : モデルの拡大度合い
```
### ContactShadows
モデルに影をつけるコンポーネント
```ts
import { ContactShadows } from "@react-three/drei";
```
```tsx
<ContactShadows position-y={-2.0} opacity={0.7} scale={7} blur={2.4} color={"#000"} far={10} resolution={256} />
// position-y : 影の向き(y)※デフォルトでは上向き 
// opacity : 影の透過度
// scale : 影の拡大度合い
// blur : 影のぼかし具合
// color : 影の色
// far :
// resolution : 影の解像度
```
[docs](https://github.com/pmndrs/drei#contactshadows)
### Float
このコンポーネントを使用するとでものような浮いてるような表現ができます。
```ts
import { Float } from "@react-three/drei";
```
```tsx
<Float
  speed={1} // 回転速度。デフォルトでは1
  rotationIntensity={1} // 回転強度。デフォルトでは1
  floatIntensity={1} // 上下の Float の強度。デフォルトでは1
  floatingRange={[1, 10]} // オブジェクトが浮動する Y 軸値の範囲。デフォルトは [-0.1,0.1]
>
  <mesh />
</Float>
```
### Text
文字を表示するコンポーネント
```ts
import { Text } from "@react-three/drei";
```
```tsx
<Text
 color={"black"} // 文字の色
 anchorX={"center"} //
 anchorY={"middle"} //
 position={[0, 0, 0]} //文字のポジショニング
>
  hello world!
</Text>
```
[docs](https://github.com/pmndrs/drei#text)
### PresentationControls
スナップバックなどを備えた**Semi-OrbitControls**です。
これらのコントロールは **カメラを回転させませんが、コンテンツを回転させます** 。OrbitControl のように限界に達したときに突然停止するのではなく、停止位置をスムーズに予測します。
```ts
import { PresenrionControls } from "@react-three/drei";
```
```tsx
<PresentationControls
  enabled={true} // これを false に設定するとコントロールを無効にできる。
  global={false} // これを true に設定するとグローバルに回転する。
  cursor={true} // ドラッグ時にカーソルのスタイルを切り替える。
  config={{ mass: 1, tension: 170, friction: 26 }} // スプリングの設定。
  snap={false} // 中央にスナップバックします。指定方法は config と同じ。
  speed={1} // カメラを操作した時のモデルの動きの速度。
  zoom={1} // 極最大値の半分に達したときのズーム倍率
  rotation={[0, 0, 0]} // デフォルトの回転度合い
  polar={[0, Math.PI / 2]} // 垂直方向の制限
  azimuth={[-Infinity, Infinity]} // 水平方向の制限
  domElement={events.connected} // このコントローラの DOM 要素イベントがアタッチされます。
>
  <mesh />
</PresentationControls>
```
[docs](https://github.com/pmndrs/drei#presentationcontrols)
## テクスチャ
https://codesandbox.io/embed/r3f-texture-5v8mqw?fontsize=14&hidenavigation=1&theme=dark

ジオメタリに画像(テクスチャ)を貼る方法をご紹介します。`useTexture()`と呼ばれるものがdreiに存在するため、使用します。
```ts
import { useTexture } from "@react-three/drei";
```
```tsx
const maping = useTexture("画像パス");

return <meshStandardMaterial map={texture} />
```
[docs](https://github.com/pmndrs/drei#usetexture)
## パララックス
r3fを使えば、パララックスっぽいことも簡単に実現することができます。

https://codesandbox.io/embed/r3f-scroll-animation-nz7wcv?fontsize=14&hidenavigation=1&theme=dark

### useThree()
dreiではなくr3fにあるHooks。
デフォルトのレンダラー、シーン、カメラなどが含まれる状態モデルにアクセスできます。また、画面内のキャンバスの現在のサイズとビューポートの座標も表示されます。

```tsx
import { useThree } from '@react-three/fiber'

function Foo() {
   const { whidth, height } = useThree((state) => state.viewport);
```
ビューポートの width と height を取得する例です。
[docs](https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree)

### Image
シェーダーベースの画像コンポーネント
```ts
import { Image } from "@react-three/drei";
```
```tsx
<Image
 url="画像パス"
 scale={[1, 1, 1]} // 画像の拡大度合い
 position={[0, 0, 0]} // 画像のポジショニング
 transport
 opacity={1} // 画像の不透明度
/>
```
[docs](https://github.com/pmndrs/drei#image)

### ScrollControls、Scroll
スクロールをすることが可能になるコンポーネント。
```ts
import { ScrollControls, Scroll } from "@react-three/drei";
```
```tsx
<ScrollControls 
 pages={2} //　　スクロール量。
 damping={2} // 慣性。
 horizontal={false} // true にすると横スクロール。
 infinite={false} // true にすると無限スクロール(ループ)。
 eps={0.00001} // スクロールの精度。デフォルトでは、0.00001。
 distance={0.2} // 秒単位の摩擦。デフォルトでは、0.2。
 maxSpeed={0.1} // 速度を 1 秒あたり 0.1 単位に固定
>
  <Scroll>
    <Images />
  </Scroll>
  <Scroll html>
    <h1 style={{ position: "absolute", top: "60vh", left: "1.5em" }}>
      Hello
    </h1>
    <h1 style={{ position: "absolute", top: "140vh", left: "20vw" }}>
      Would!!
    </h1>
  </Scroll>
</ScrollControls>
```
`<Scroll />`で html をラップする際は`<Scroll html />`とする。

[docs](https://github.com/pmndrs/drei#scrollcontrols)

### useScroll()
現在のスクロールなどの有用なデータを提供するフック
```ts
import { useScroll } from "@react-three/drei";
```
```tsx
const Images = () => {
  const { height } = useThree((state) => state.viewport);
  const group = useRef({} as any);
  const data = useScroll();

  useFrame(() => {
    group.current.children[0].material.zoom = 1 + data.range(0, 1 / 3) / 3;
    group.current.children[1].material.zoom = 1 + data.range(0, 1 / 3) / 3;
    group.current.children[2].material.zoom =
      1 + data.range(1.15 / 3, 1 / 3) / 3;
    group.current.children[3].material.zoom =
      1 + data.range(1.15 / 3, 1 / 3) / 3;
  });
  return (
    <group ref={group}>
      <Image
        url="画像パス"
        scale={[4, height, 1]}
        position={[-1, 0, 1]}
      />
      <Image
        url="画像パス"
        scale={3}
        position={[2, 0, 1]}
      />
      <Image
        url="画像パス"
        scale={[1, 2.7, 1]}
        position={[-2.3, -height, -2]}
      />
      <Image
        url="画像パス"
        scale={[1.4, 2, 1]}
        position={[1.3, -height - 0.3, 1.2]}
      />
    </group>
  );
};
export default Images;
```
## さいごに
いかがでしたか？このように、r3fとdreiを組み合わせることで表現できることが増えるので、r3fを使用する際にはdreiと一緒に使ってみてください。
また。dreiにあるメソッドの数は膨大にあるため、紹介しきれなかったものに関しては[ドキュメント](https://github.com/pmndrs/drei#readme)を読むなりしながら各自で調べてみてください。

最後まで読んでいただいてありがとうございました。