⚡️ Limited Black Friday Deal
Get 50% off on the React Three Fiber Ultimate Course with the promo code ULTIMATE50
Buy Now
Fundamentals
Core
Master
Shaders
Render Target
Quando usamos React Three Fiber simplesmente adicionamos um componente Canvas
e colocamos nossa cena 3D dentro dele e ele fará a renderização na tela.
Se você tem alguma experiência com Three.js, sabe que primeiro precisamos criar um WebGLRenderer
e depois chamar renderer.render(scene, camera)
para renderizar nossa cena na tela.
Felizmente, React Three Fiber faz tudo isso por nós nos bastidores, mas para usos mais avançados, é importante saber como isso funciona.
O papel do renderer é processar a cena 3D para criar a imagem 2D que vemos na tela.
Por padrão, a saída do renderer é configurada para o componente Canvas
e é exibida na tela, mas também podemos direcioná-la para uma textura (via um WebGLRenderTarget
).
Por enquanto, isso pode ser um pouco conceitual, então vamos ver como isso funciona na prática e em quais maneiras criativas podemos usá-lo.
Câmera de Segurança
Para entender como os Render Targets funcionam e o que podemos fazer com eles, preparei uma cena 3D contendo:
- Um modelo 3D de sala de estar por Alex Safayan CC-BY via Poly Pizza
- Um avatar do Ready Player Me (como o que usamos nas lições do portfólio) assistindo Bob Esponja na TV
- Um controle remoto 2D com múltiplos botões
O objetivo é criar um sistema de vigilância renderizando a cena a partir do ponto de vista de uma câmera de segurança na TV.
Renderizando a cena para uma textura
Para renderizar nossa cena atual para uma textura, precisaremos criar um Render Target.
Graças à biblioteca Drei, podemos facilmente criar um Render Target com o hook useFBO
:
// ... import { useFBO } from "@react-three/drei"; export const Experience = () => { const cornerRenderTarget = useFBO(); // ... };
Se você quiser saber como criar um Render Target do zero, você pode conferir o código fonte do hook
useFBO
aqui. É um bom hábito para entender como as coisas funcionam por baixo dos panos.
Agora que temos um Render Target, precisamos informar ao nosso renderer para renderizar nossa scene
usando nossa camera
.
Todos esses objetos estão disponíveis no root state da nossa aplicação React Three Fiber. Este é o objeto retornado pelo hook useThree
.
A propriedade
gl
é o renderer.
Mas, como queremos exibir o que está acontecendo em tempo real na TV, faremos o processo a cada frame usando o hook useFrame
.
A função de callback inclui o root state para que possamos acessar nossas variáveis diretamente dele:
// ... import { useFrame } from "@react-three/fiber"; export const Experience = () => { // ... useFrame(({ gl, camera, scene }) => { gl.setRenderTarget(cornerRenderTarget); gl.render(scene, camera); gl.setRenderTarget(null); }); // ... };
O que estamos fazendo aqui é:
- Definindo o Render Target como a saída do renderer
- Renderizando a
scene
usando acamera
e, como a saída do renderer está definida para o Render Target, ele renderizará a cena nele - Definindo a saída do renderer para
null
para renderizar a cena na tela novamente
Agora que temos nosso Render Target, precisamos exibi-lo na TV. Vamos adicionar uma referência ao material que está renderizando o vídeo:
// ... 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> {/* ... */} </> ); };
E então, podemos usar o hook useFrame
para atualizar a propriedade map
do material com nosso Render Target:
// ... export const Experience = () => { // ... useFrame(({ gl, camera, scene }) => { // ... tvMaterial.current.map = cornerRenderTarget.texture; }); // ... };
Agora podemos ver a visão da câmera de segurança na TV, mas a tela da TV na visão da câmera de segurança está vazia.
Isso ocorre porque, quando renderizamos a cena para o Render Target, nossa tela está vazia.
Efeito de Inception
Para ver a visão da câmera de segurança dentro da tela da TV da tela da TV 🤯, precisamos:
- Criar outro render target (vamos nomeá-lo de
bufferRenderTarget
) - Renderizar a cena nele
- Anexá-lo ao material da tela da TV
- Renderizar a cena no
corderRenderTarget
- Anexá-lo ao material da TV
// ... 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; }); // ... };
Pode parecer assustador, mas na verdade é bem simples. Apenas pense sobre o que está acontecendo em cada passo.
Agora nossa cena é infinitamente renderizada dentro da tela da TV!
Mesmo que seja o efeito mais realista que podemos criar, não é o mais criativo. Vamos remover o efeito de inception e exibir Spongebob Squarepants na tela em vez disso:
End of lesson preview
To get access to the entire lesson, you need to purchase the course.