🥽

Reactで書いたWebサイトにシンプルな3Dモデルを埋め込んでアニメーションさせてみた

2023/12/23に公開

はじめに

こんにちは!Iwaken Lab.のにっしです!
最近はWebXR触ったり、React書いたりしてる民です。

今回は、私が立ち上げた山口県のテックコミュニティ「Code for Yamaguchi」のサイトを3D表現つきでリニューアルして、そのサイトが想像以上に簡単にWeb3Dの表現ができたので、実装例を紹介していきます。

作ったもの

https://codeforyamaguchi.org/

本サイトは、テックコミュニティとして技術に興味ある地元の方が興味を持つよう、「シンプルかつかっこ良いサイトを作りたい」「コードを触れなくても全員で適宜コンテンツを編集したい」 という思いでリニューアルしました。前半は、R3Fを使ってシンプルな3Dモデルを埋め込むことで解決を図り、後半はNotionをヘッドレスCMSとして用いることで解決を図りました。(今回ヘッドレスCMSについては主題ではないため、言及しません)

また、GitHubリポジトリをこちらで公開しています。
https://github.com/Code-for-Yamaguchi/codeforyamaguchi.org

3Dモデルを埋め込むには?

Webブラウザ上で3Dモデルを表示させるための標準仕様として、WebGL(Web Graphics Library)というものがあります。

WebGlは、スマートフォンを含む全てのブラウザに対応しており、Webという誰でもスムーズにアクセス可能な空間で表示できるため多くのユーザーにリッチな体験を届けることが可能です。

https://developer.mozilla.org/ja/docs/Web/API/WebGL_API

また、最近では、WebGPU(Web Graphics Processing Unit)というWebGLの後継にあたるAPIも標準実装が進められています。(現状は一部ブラウザのみ対応)

https://developer.mozilla.org/ja/docs/Web/API/WebGPU_API

そして、これらを効率よく扱うためのWebGLライブラリとして、Three.js、Babylon.js、PlayCanvasなどが台頭し、簡単にJavaScriptで扱えるようになっています。

https://threejs.org/
https://www.babylonjs.com/
https://playcanvas.com/

ReactでWebGLを扱うためには?

最近、多くのサイトはReact/Vue/Svelteなどの宣言的UIを用いた開発が多くなってきています。私も、Webフロントエンドの実装においては、ほぼReactを用いて実装しています。
なので、宣言的UIでWebGLを扱うために、各WebGLライブラリが対応したライブラリが公開されています。
React×Three.jsではReact Three Fiber(通称R3F)というライブラリが有名です。今回は、R3Fを採用し、実装していきます。

https://docs.pmnd.rs/react-three-fiber/getting-started/introduction

採用技術

ということでベース技術の紹介が終わったところで、今回実装するにあたって採用した技術スタックを掲載します。本記事ではR3Fについてのみ主軸を置いて解説していきます。

  • Next.js
  • TypeScript
  • Three.js
  • React Three Fiber(R3F)
  • Framer Motion
  • shadcn/ui
  • Notion SDK for JavaScript
  • Cloudflare Pages

実装

シンプルなモデルをサイトに埋め込むにあたり、実装としては以下の流れで行います。

  1. 3Dモデルを作成し、.glbでエクスポート
  2. .glbを読み込むReactコンポーネントを実装
  3. 3Dモデル表示部全体のReactコンポーネントを実装

1. 3Dモデルを作成し、.glbでエクスポート

1については、Blender等の3Dモデリングソフトを扱える方はモデリングしてください。
また、Blender触れないよ!という方向けに、SplineというWebブラウザで簡単に3Dモデリング可能なサービスを紹介します。
https://spline.design/

簡単にチュートリアルを見ながらモデリングしてもらったり、以下のようなデフォルトのライブラリからモデルを取ってきてglbエクスポートをしてみるのも良いでしょう。

今回のサイトにおいては、他のコミュニティメンバーがBlenderで作ってくれた.glbモデルを使っていきます。

2. .glbを読み込むReactコンポーネントを実装

.glbを読み込むコンポーネントを実装していきます。ここでは、GLTFをJSXのコンポーネントに変換してくれるCLIツール@react-three/gltfjsxを使ってコンポーネントを生成します。

https://github.com/pmndrs/gltfjsx

TypeScriptで生成するには、以下のコマンドを実行します。

npx gltfjsx fuku-san.glb -t

すると、以下のようなコードが生成されます。
https://github.com/Code-for-Yamaguchi/codeforyamaguchi.org/blob/develop/src/components/fuku3D/Fuku-san.tsx

3. 3Dモデル表示部全体のReactコンポーネントを実装

最後に、3Dモデル表示部のReactコンポーネントを書いていきます。
@react-three/dreiのCanvasやEnvironmentなどのコンポーネントを使うことで、ライトやシャドウを含めた 3D空間を簡単に構築できます。
※ちなみに、dreiとはドイツ語で数字の"3"を指す言葉らしいです。

https://github.com/pmndrs/drei

ドキュメントを見ながら組み合わせていくことで、簡単に3Dモデルを埋め込んだサイトの実装が可能です。

https://github.com/Code-for-Yamaguchi/codeforyamaguchi.org/blob/develop/src/components/fuku3D/index.tsx

また、アニメーション部分については以下の関数で実装しています。
useFrameは、レンダリングされたフレームごとに実行するhooksです。

  1. state.clock.elapsedTime * 0.3
    アニメーションの経過時間(秒)を0.3倍した値を返します。時間の進行に対する反応性を調整します。

  2. Math.sin(state.clock.elapsedTime * 0.3)
    先ほどの値を正弦波に変換します。正弦波は、-1から1までの値を周期的に生成するため、結果として得られるrotationYの値も時間とともに周期的に変化します。

  3. (Math.sin(state.clock.elapsedTime * 0.3) * Math.PI) / 3

先ほどの値にMath.PIを掛けることで、値の範囲を-πからπに拡大します。最後に、全体を3で割ることで、値の範囲を-π/3からπ/3に縮小します。つまり、±60°の間で回転するアニメーションになります。

https://github.com/Code-for-Yamaguchi/codeforyamaguchi.org/blob/7908b05eb8e509ce31c5c979f59205e2b2bcf467/src/components/fuku3D/index.tsx#L57C1-L73C2

まとめ

今回は、「Reactで書いたWebサイトにシンプルな3Dモデルを埋め込んでみた」ということで記事を書いてきました。コミュニティであったり、自分の3Dモデルを作って、簡単に埋め込んでみるだけで、少しリッチな体験を与えられるWebサイトができる!すごいのでみんなやろう!というお話でした。

3Dの領域ってなかなかWeb開発者が入りにくいとこだと思うのですが、やってみると意外と簡単に実装できるようなエコシステムが整っています。(どっちかというと問題は3Dモデリングの方...😇)

AppleVision Proをはじめ、空間コンピューティングがより流行っていく時代、コンテンツの主流は2Dから3Dへと移り変わっていくような流れになっていくと確信しています。
その流れにWebほど簡単に対応できる分野はあまり多くはないのではないでしょうか?この機会に少しでもWeb3Dに興味を持ってくれる人が増えるといいなあ、と思って書いたアドベントカレンダー(遅刻)でした。

https://codeforyamaguchi.org/

Discussion