【R3F】R3Fでカメラのピンボケをエモくしてみた話

# ⚫️ はじめに

React Three Fiberで、マウント時にイージングに対応してカメラの焦点を合わせる演出を実装してみました。これを応用すれば、エモい導入演出を実装することができます。

# ⚫️ 自己紹介
初めまして。趣味でweb開発を勉強している273*(ﾂﾅｻﾝﾄﾞ) / Kei.と申します。フルスタック開発やツール制作を楽しんでいる人です。

# ⚫️ 完成品

![monkey.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3530539/9606c942-2a0f-455f-a6e3-94aedb8c6ffc.gif)

同様の効果を私のポートフォリオでも実装しています。ぜひ覗いてみてください。

https://273doworks.com

# ⚫️ 実装
実装はかなりシンプルで、ボケエフェクト(被写界深度)のパラメータをうまく変更するだけです。変数の状態更新をするだけではうまく行かないので工夫が必要です。

今回作成したソースコードのリポジトリです。

https://github.com/273Do/R3F-DepthOfField-Easing

### 1. カメラのピンボケの実装
事前にR3Fのインストールとオブジェクトやカメラの設定をしておきます。
postprocessingのライブラリをインストールして`<Canvas>`内にボケエフェクトを適応します。

https://react-postprocessing.docs.pmnd.rs/introduction

```
npm i @react-three/postprocessing
```

```ts
<EffectComposer multisampling={8}>
  <DepthOfField
    focusDistance={30}
    focalLength={0.02}
    bokehScale={80}
  />
  <Noise opacity={0.7} premultiply />
</EffectComposer>
```

 カメラのボケ具合は**bokehScale**で設定できます。今回は80に設定しています。追加でノイズを少し載せておくと映えます。

![スクリーンショット 2025-04-15 23.30.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3530539/2907142c-3ed1-4771-b4fb-54a8a93b42bc.png)


### 2. アニメーション的に変化する値の作成

ピンボケから焦点を合わせる表現は、カメラのボケ具合の値を80から0に変化させれば実装できそうですね。先述のとおり、状態管理ではボケ具合が変化しないので、アニメーションライブラリである**React Spring**を使用していきます。

https://www.react-spring.dev/docs/guides/react-three-fiber

```
npm i @react-spring/three 
```

useSpringフックを利用して、アニメーション的に変化する値を生成します。
イージングはreact springのものを使用します。

```ts
  const springs = useSpring({
    from: { blurScale: 80 },
    to: { blurScale: 0 },
    config: {
      duration: 600,
      easing: easings.easeOutCirc,
    },
  });
```

初期値は80で、0へ収束するように設定します。
また、以下のリンクからイージングを参考にします。イージング、ベジェ曲線については趣旨ではないので解説を省きます。映像制作などにもよく使い、奥深いので是非調べてみてください。今回は**easeOutCirc**を使用します。**easeOutExpo**もおすすめですが、シーンに合うものを選びましょう。
アニメーション時間は600ミリ秒を指定しています。

https://easings.net/ja

これで、イージングに沿った動きと指定の時間で値を変化させられるようになりました。値自体は```springs.blurScale```のようにして取り出します。

### 3. コンポーネントへ値を適応する

```springs.blurScale```をこのまま```bokehScale={}```へ格納しても動作しません。

`<DepthOfField/>`をanimated関数を使用して、アニメーション表示を自律的に行うコンポーネント([アニメーション化されたコンポーネント](https://qiita.com/uehaj/items/260f188851045cc091ac#%E7%94%A8%E8%AA%9E%E8%AA%AC%E6%98%8E%E3%82%A2%E3%83%8B%E3%83%A1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E5%8C%96%E3%81%95%E3%82%8C%E3%81%9F%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88animated-component))に変換してあげます。

```ts
import {
  DepthOfField as BaseDepthOfField,
  EffectComposer,
  Noise,
} from "@react-three/postprocessing";

const DepthOfField = animated(BaseDepthOfField);
```

そして`springs.blurScale`を格納してあげることでようやく機能します。

```ts
<EffectComposer multisampling={8}>
  <DepthOfField
    focusDistance={30}
    focalLength={0.02}
    bokehScale={springs.blurScale}
  />
  <Noise opacity={0.7} premultiply />
</EffectComposer>
```

![monkey.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3530539/9606c942-2a0f-455f-a6e3-94aedb8c6ffc.gif)

いい感じですね！動きの滑らかさは**linear**(等速)と比較してみてください。一目瞭然です。

# ⚫️ おまけ

他のエフェクトにも適応してみます。

- Dot Screen
![mon2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3530539/15610e7d-e57d-4144-8a80-c9a6c6f0e255.gif)

- Pixelation
![mo3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3530539/74686d85-9cd8-47e3-a8ba-9949e4150799.gif)


# ⚫️ 最後に

いかがでしたでしょうか。以前もR3Fの実装記事を書きましたが、いまだに自力で実装するのに苦戦しています。今回のように簡単にリッチなエフェクトを実装できるのはありがたいですね。ちなみに登場したモデルはBlenderマスコットのSuzanne君です。
最後まで読んでいただきありがとうございました！それでは！

# ⚫️ 参考記事

https://qiita.com/uehaj/items/260f188851045cc091ac