react-three-fiber の ecosystem を使って Box をたくさん散布した
はじめに
先日お客さまの勉強会に参加し react-three-fiber を教えていただきました。
ecosystem を見ていると
- react と threejs の adapter を始め
- threejs の helper
- UI ライブラリ
- shader の helper
- 状態管理
など幅広くサポートしています。
この中から以下のライブラリを使って react-three-fiber を試してみようと考えました。
"dependencies": {
"@react-three/drei": "^9.108.1",
"@react-three/fiber": "^8.16.8",
"@react-three/postprocessing": "^2.16.2",
"miniplex": "^2.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"three": "^0.166.1",
"zustand": "^4.5.4"
}
love-poimandres
以下のリンクは動作しているサンプルです。
最初に生成された白い Box は不死です。
タッチしている間 Box が生成され時間経過で死んでいきます。
libraries
使用するライブラリを紹介します。
react, react-dom, three
は基盤ですので省略します。
@react-three/fiber
React Three Fiber は React のレンダラーで、Three.js を React で扱いやすくするためのライブラリです。Three.js のシーンを React コンポーネントとして記述できるようになり、宣言的に 3D グラフィックスを作成できます。
@react-three/drei
React Three Fiber の上に構築された便利機能です。
テキスト、コントロール、ローダーなど、よく使用される機能を簡単に実装できます。
@react-three/postprocessing
Three.js のポストプロセッシング効果を React Three Fiber で簡単に使用できるようにするライブラリです。ブルーム、グロー、被写界深度などの視覚効果を簡単に追加できます。
miniplex
軽量なエンティティコンポーネントシステム(ECS)ライブラリです。大量のオブジェクトを効率的に管理し、パフォーマンスを最適化するのに役立ちます。
ECS とは?
先日 Unity6 の発表でも大きく取り上げられましたが Entity Component System という大量のオブジェクトを効率的に扱うアーキテクチャです。
ゲームやシミュレーションの世界では、たくさんのオブジェクトを扱います。
これは Vampire Survivors というやり始めたらあっという間に1日が終わる狂気のゲームです。
見てください。この大量の敵とエフェクトを。
ダメージ表記もありますし、自分の攻撃も大量に出るので、膨大な数の処理を行っていることがわかります。
Vampire Survivors が ECS を採用しているかは定かではありませんが、このような大量の処理を効率的に処理することができます。
- Entity: ID のみを保有しているオブジェクトの実体
- Component: 位置や速度など Entity のパラメータ
- System: Component ゲームロジックなど振る舞いを設定する
このようにデータと処理を完全に分離することで
- 柔軟性抜群: 新しい機能を追加するのも、既存の機能を変更することが楽
- パフォーマンス向上: データをまとめて処理できるから効率的
- デバッグしやすい: データと処理が分かれてるから、問題の切り分けがしやすい
という効果が期待できます。
zustand
状態管理ライブラリです。
Redux に近いですが、状態管理機能をより簡潔に実装することができます。
アプリケーションの状態(マウスの位置、ドラッグ状態)を効率的に管理し、コンポーネント間で共有するために使いました。
コードの説明
状態管理 (Zustand)
export const useAppStore = create<AppState>((set) => ({
mousePosition: new Vector3(),
isDragging: boolean,
setMousePosition: (position: Vector3) => set({ mousePosition: position }),
setIsDragging: (isDragging: boolean) => set({ isDragging }),
addAgents: () => {
// エージェントを追加するロジック
}
}))
Zustand を使用してアプリケーションの状態を管理しています。マウスの位置、ドラッグ状態、エージェントの追加機能などをグローバルに管理しています。
エンティティ管理 (Miniplex)
const world = new World<Agent>()
Miniplex を使用して Agent(Box) を管理しています。これにより、大量のAgent を効率的に扱うことができます。
シェーダーマテリアル (@react-three/drei)
const GlowingParticleMaterial = shaderMaterial(
{},
`
varying vec3 vColor;
void main() {
vColor = instanceColor;
vec4 mvPosition = modelViewMatrix * instanceMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * mvPosition;
}
`,
`
varying vec3 vColor;
void main() {
gl_FragColor = vec4(vColor, 1.0);
}
`
)
@react-three/drei の shaderMaterial を使用してカスタムシェーダーを定義しています。これにより、各 Box に独自の色を適用できます。
メインコンポーネント (React Three Fiber)
React Three Fiber を使用してシーンを構築しています。<Canvas>
内に3Dオブジェクトやエフェクトを配置しています。
@react-three/postprocessing を使用して、グリッチ、ブルーム、ノイズ、ビネットなどの視覚効果を追加しています。
手続き型になりがちな 3D プログラミングを React を構築するような感覚で宣言的にコーディングできるのがとてもいいですね。
export function App() {
// ...
return (
<Canvas camera={{ position: [0, 0, 15], fov: 60 }}>
<color attach='background' args={['#5e595d']} />
<Swarm />
<InteractionTracker />
<Text>Poimandres</Text>
<EffectComposer>
<Glitch />
<Bloom />
<Noise />
<Vignette />
</EffectComposer>
</Canvas>
)
}
使ってみた感想
react-three-fiber
やはり宣言的に記述できるのは、とても読みやすく書きやすかったです。
ただインタラクティブなアプリを作るなら手続き的に書きたくなるケースが多く、生の threejs が使いたくなるケースもあるだろうなと感じました。
postprocessing
react-three-fiber を触っていて一番面白かった部分です。
<EffectComposer>
内に <Vignette />
と追加するだけでビネットがつけられたとき感動しました。
zustand
zustand の知識と経験不足で主観が強いですが、redux の方が好きでした。
redux-toolkit を使用した slice(stateの分割) の整理や redux saga を使用した手続き的な開発が魅力的です。
zustand の middleware を開発することで redux くらい機能豊富でより簡潔な state 管理ができるかもしれません。
miniplex
大量のオブジェクトを完結に操作できるということはとてもいいのですが、描画とオブジェクト管理が別物である以上あまりパフォーマンスの面では効果を感じることができませんでした。
bevy のような wasm でならば操作性とパフォーマンスを両立させられるかもしれません。
アーキテクチャ自体はすごくすきです。
今後も調べたり触っていこうと思います。
PR
株式会社 blue で開発に携わっている VOTE は、二択のお題に投票する Web アプリです。
技術的な話題から日常の選択など、多様なお題でお気軽に遊んでみてください。
株式会社blueの開発ブログです。新しいヒントや可能性を探しつつ検証にも熱く励んでおります。読んでいる方にも、弊社の記事から新しいキッカケや可能性を見つけてもらえたらと思います。開発者ブログ以外のblueの思いもこちらにて配信中→note.com/blueteam/
Discussion