Render Target

Starter pack

Quando usiamo React Three Fiber aggiungiamo semplicemente un componente Canvas e inseriamo la nostra scena 3D all'interno, e verrà renderizzata sullo schermo.

Se hai qualche esperienza con Three.js, saprai che prima dobbiamo creare un WebGLRenderer e quindi chiamare renderer.render(scene, camera) per renderizzare la nostra scena sullo schermo.

Fortunatamente, React Three Fiber fa tutto questo per noi sotto il cofano, ma per casi d'uso più avanzati, è importante sapere come funziona.

Il ruolo del renderer è di elaborare la scena 3D per creare l'effettiva immagine 2D che vediamo sullo schermo.

Per impostazione predefinita, l'output del renderer è impostato sul componente Canvas ed è visualizzato sullo schermo, ma possiamo anche indirizzarlo verso una texture (tramite un WebGLRenderTarget).

Per ora può sembrare un po' concettuale, vediamo come funziona in pratica e in quali modi creativi possiamo utilizzarlo.

Telecamera di Sicurezza

Per capire come funzionano i Render Target e cosa possiamo fare con essi, ho preparato una scena 3D contenente:

  • Un modello 3D di salotto di Alex Safayan CC-BY tramite Poly Pizza
  • Un avatar Ready Player Me (come quello usato nelle lezioni di portfolio) che guarda SpongeBob SquarePants in TV
  • Un telecomando 2D con più pulsanti

3D avatar che guarda la TV

L'obiettivo è creare un sistema di sorveglianza rendendo la scena dal punto di vista di una telecamera di sicurezza sulla TV.

Rendering della scena su una texture

Per renderizzare la nostra scena attuale su una texture, avremo bisogno di creare un Render Target.

Grazie alla libreria Drei, possiamo facilmente creare un Render Target con il hook useFBO:

// ...
import { useFBO } from "@react-three/drei";

export const Experience = () => {
  const cornerRenderTarget = useFBO();
  // ...
};

Se vuoi sapere come creare un Render Target da zero, puoi controllare il codice sorgente del hook useFBO qui. È una buona abitudine da avere per capire come funzionano le cose sotto il cofano.

Ora che abbiamo un Render Target, dobbiamo indicare al nostro renderer di renderizzare la nostra scene usando la nostra camera.

Tutti quegli oggetti sono disponibili nello stato root della nostra applicazione React Three Fiber. Questo è l'oggetto restituito dal hook useThree.

La proprietà gl è il renderer.

Tuttavia, poiché vogliamo visualizzare in tempo reale ciò che sta accadendo sulla TV, eseguiremo il processo ad ogni frame usando il hook useFrame.

La funzione callback include il root state in modo da poter accedere direttamente alle nostre variabili:

// ...
import { useFrame } from "@react-three/fiber";

export const Experience = () => {
  // ...
  useFrame(({ gl, camera, scene }) => {
    gl.setRenderTarget(cornerRenderTarget);
    gl.render(scene, camera);
    gl.setRenderTarget(null);
  });
  // ...
};

Quello che stiamo facendo qui è:

  • Impostare il Render Target come output del renderer
  • Renderizzare la scene usando la camera e poiché l'output del renderer è impostato sul Render Target, renderizzerà la scena su di esso
  • Impostare l'output del renderer su null per renderizzare la scena di nuovo sul canvas

Ora che abbiamo il nostro Render Target, dobbiamo visualizzarlo sulla TV. Aggiungiamo un riferimento al material che attualmente rende il video:

// ...
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 quindi, possiamo usare il hook useFrame per aggiornare la proprietà map del material con il nostro Render Target:

// ...
export const Experience = () => {
  // ...
  useFrame(({ gl, camera, scene }) => {
    // ...
    tvMaterial.current.map = cornerRenderTarget.texture;
  });
  // ...
};

Avatar 3D che guarda la TV con la vista della telecamera di sicurezza

Ora possiamo vedere la vista della telecamera di sicurezza sulla TV, ma lo schermo della TV nella vista della telecamera di sicurezza è vuoto.

Questo accade perché quando rendiamo la scena sul Render Target, il nostro schermo è vuoto.

Effetto Inception

Per vedere la visuale della telecamera di sicurezza all'interno dello schermo del televisore dello schermo del televisore 🤯, dobbiamo:

  • Creare un altro render target (lo chiameremo bufferRenderTarget)
  • Renderizzare la scena su di esso
  • Allegarlo al material dello schermo della TV
  • Renderizzare la scena al corderRenderTarget
  • Allegarlo al material della 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;
  });
  // ...
};

Potrebbe sembrare spaventoso, ma in realtà è abbastanza semplice. Basta pensare a cosa sta succedendo in ogni passaggio.

Ora la nostra scena è riprodotta infinitamente all'interno dello schermo della TV!

Anche se è l'effetto più realistico che possiamo creare, non è il più creativo. Rimuoviamo l'effetto inception e visualizziamo Spongebob Squarepants sullo schermo invece:

Three.js logoReact logo

React Three Fiber: The Ultimate Guide to 3D Web Development

✨ You have reached the end of the preview ✨

Go to the next level with Three.js and React Three Fiber!

Get full access to this lesson and the complete course when you enroll:

  • 🔓 Full lesson videos with no limits
  • 💻 Access to the final source code
  • 🎓 Course progress tracking & completion
  • 💬 Invite to our private Discord community
Unlock the Full Course – Just $85

One-time payment. Lifetime updates included.