Fundamentals
Core
Master
Shaders
Render Target
React Three Fiberを使用すると、単にCanvas
コンポーネントを追加し、3Dシーンを中に配置すると、それが画面にレンダリングされます。
Three.jsの経験がある場合、最初にWebGLRenderer
を作成し、次にrenderer.render(scene, camera)
を呼び出してシーンを画面にレンダリングする必要があることを知っています。
幸いなことに、React Three Fiberはこれをすべて裏で処理してくれますが、より高度な使用例のためには、その仕組みを知っておくことが重要です。
rendererの役割は、3Dシーンを処理して、画面に表示される実際の2Dイメージを作成することです。
デフォルトでは、rendererの出力はCanvas
コンポーネントに設定され、画面に表示されますが、テクスチャ(WebGLRenderTarget
経由)に出力することもできます。
今のところ、これは少し概念的なものかもしれませんが、実際にどのように機能するのか、そしてどのような創造的な方法でそれを使用できるのかを見てみましょう。
セキュリティカメラ
Render Targetsがどのように機能し、それを使って何ができるかを理解するために、以下を含む3Dシーンを用意しました:
- Alex Safayanによるリビングルーム3Dモデル CC-BY via Poly Pizza
- テレビでスポンジボブを見ているReady Player Meアバター(portfolioレッスンで使用するものと同様)
- 複数のボタンが付いた2Dリモコン
目標は、シーンをセキュリティカメラの視点からテレビにレンダリングすることによって、監視システムを作成することです。
シーンのテクスチャへのレンダリング
現在のシーンをテクスチャにレンダリングするためには、Render Targetを作成する必要があります。
Drei libraryのおかげで、useFBO
フックを使用して簡単にRender Targetを作成できます:
// ... import { useFBO } from "@react-three/drei"; export const Experience = () => { const cornerRenderTarget = useFBO(); // ... };
Render Targetをゼロから作成する方法を知りたい方は、
useFBO
フックのソースコードをこちらで確認できます。裏でどのように動作しているかを理解することは良い習慣です。
Render Targetを作成したら、scene
をcamera
を使ってレンダリングするためにrendererに指示する必要があります。
これらすべてのオブジェクトはReact Three Fiberアプリケーションのroot stateにあります。これはuseThree
フックによって返されるオブジェクトです。
gl
プロパティがrendererです。
しかし、テレビにリアルタイムで表示したいので、useFrame
フックを使用して各フレームでこのプロセスを行います。
コールバック関数にはroot stateが含まれているので、変数に直接アクセスできます:
// ... import { useFrame } from "@react-three/fiber"; export const Experience = () => { // ... useFrame(({ gl, camera, scene }) => { gl.setRenderTarget(cornerRenderTarget); gl.render(scene, camera); gl.setRenderTarget(null); }); // ... };
ここで行っていることは以下の通りです:
- Render Targetをrendererの出力として設定する
camera
を使用してscene
をレンダリングする。rendererの出力がRender Targetに設定されているので、シーンがそれにレンダリングされる- rendererの出力を
null
に設定して、再びキャンバスにシーンをレンダリングする
Render Targetが作成されたので、次にこれをテレビに表示する必要があります。現在ビデオをレンダリングしているmaterialへの参照を追加します:
// ... import { useRef } from "react"; export const Experience = () => { // ... const tvMaterial = useRef(); // ... return ( <> {/* ... */} <group position-y={-0.5}> <group> <Sky /> <Avatar rotation-y={Math.PI} scale={0.45} position-z={0.34} /> <Gltf src="models/Room.glb" scale={0.3} rotation-y={-Math.PI / 2} /> <mesh position-x={0.055} position-y={0.48} position-z={-0.601}> <planeGeometry args={[0.63, 0.44]} /> <meshBasicMaterial ref={tvMaterial} /> </mesh> </group> </group> {/* ... */} </> ); };
そして、useFrame
フックを使用してmaterialのmap
プロパティをRender Targetで更新します:
// ... export const Experience = () => { // ... useFrame(({ gl, camera, scene }) => { // ... tvMaterial.current.map = cornerRenderTarget.texture; }); // ... };
これでセキュリティカメラのビューがテレビに表示されるようになりましたが、セキュリティカメラビューの中のテレビ画面は空です。
これは、シーンをRender Targetにレンダリングする際、画面が空だからです。
Inception effect
TV画面の中のTV画面の中の監視カメラビューを表示するためには 🤯、以下の手順が必要です:
- 別のrender targetを作成します(これを
bufferRenderTarget
と呼びます) - シーンをその中でレンダリングします
- それをTV画面のmaterialに取り付けます
- シーンを
corderRenderTarget
にレンダリングします - それをTVのmaterialに取り付けます
// ... export const Experience = () => { const bufferRenderTarget = useFBO(); // ... useFrame(({ gl, camera, scene }) => { gl.setRenderTarget(bufferRenderTarget); gl.render(scene, camera); tvMaterial.current.map = bufferRenderTarget.texture; gl.setRenderTarget(cornerRenderTarget); gl.render(scene, camera); gl.setRenderTarget(null); tvMaterial.current.map = cornerRenderTarget.texture; }); // ... };
ちょっと難しそうに見えますが、実際はとてもシンプルです。各ステップで何が起こっているかを考えてください。
これでシーンが無限にTV画面内にレンダリングされるようになりました!
現実味のあるエフェクトではありますが、一番クリエイティブなものではありません。Inceptionエフェクトを取り除いて、代わりに画面にスポンジ・ボブを表示しましょう:
End of lesson preview
To get access to the entire lesson, you need to purchase the course.