Wawa Sensei logo

Render Target

Starter pack

Quand nous utilisons React Three Fiber, nous ajoutons simplement un composant Canvas et plaçons notre scène 3D à l'intérieur, et elle sera rendue à l'écran.

Si vous avez de l'expérience avec Three.js, vous savez que nous devons d'abord créer un WebGLRenderer et ensuite appeler renderer.render(scene, camera) pour rendre notre scène à l'écran.

Heureusement, React Three Fiber fait tout cela pour nous en arrière-plan, mais pour des cas d'utilisation plus avancés, il est important de savoir comment cela fonctionne.

Le rôle du renderer est de traiter la scène 3D pour créer l'image 2D réelle que nous voyons à l'écran.

Par défaut, la sortie du renderer est définie sur le composant Canvas et est affichée à l'écran, mais nous pouvons aussi la sortir sur une texture (via un WebGLRenderTarget).

Pour l'instant, cela peut paraître un peu conceptuel, voyons comment cela fonctionne en pratique et de quelles manières créatives nous pouvons l'utiliser.

Caméra de Sécurité

Pour comprendre comment fonctionnent les Render Targets et ce que nous pouvons en faire, j'ai préparé une scène 3D contenant :

  • Un modèle 3D de salon par Alex Safayan CC-BY via Poly Pizza
  • Un avatar Ready Player Me (comme celui que nous utilisons dans les leçons de portfolio) regardant Bob l'éponge sur la TV
  • Une télécommande 2D avec plusieurs boutons

3D avatar watching to the TV

Le but est de créer un système de surveillance en rendant la scène depuis le point de vue d'une caméra de sécurité sur la TV.

Rendu de la scène sur une texture

Pour rendre notre scène actuelle sur une texture, nous devons créer une Render Target.

Grâce à la bibliothèque Drei, nous pouvons facilement créer une Render Target avec le hook useFBO :

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

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

Si vous souhaitez savoir comment créer une Render Target à partir de zéro, vous pouvez consulter le code source du hook useFBO ici. Il est toujours bon de comprendre comment les choses fonctionnent en profondeur.

Maintenant que nous avons une Render Target, nous devons dire à notre renderer de rendre notre scene en utilisant notre camera.

Tous ces objets sont disponibles dans l'état root de notre application React Three Fiber. C'est l'objet retourné par le hook useThree.

La propriété gl est le renderer.

Mais, comme nous voulons afficher ce qui se passe en temps réel sur la télévision, nous effectuerons le processus à chaque frame en utilisant le hook useFrame.

La fonction de rappel inclut l'état root afin que nous puissions accéder directement à nos variables :

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

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

Ce que nous faisons ici est :

  • Définir la Render Target comme sortie du renderer
  • Rendre la scene en utilisant la camera et comme la sortie du renderer est définie sur la Render Target, il rendra la scène là-dessus
  • Réinitialiser la sortie du renderer à null pour rendre la scène de nouveau sur le canvas

Maintenant que nous avons notre Render Target, nous devons l'afficher sur la télévision. Ajoutons une référence au material qui rend actuellement la vidéo :

// ...
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>
      {/* ... */}
    </>
  );
};

Ensuite, nous pouvons utiliser le hook useFrame pour mettre à jour la propriété map du material avec notre Render Target :

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

3D avatar watching to the TV with the security camera view

Nous pouvons maintenant voir la vue de la caméra de sécurité sur la télévision, mais l'écran de la télévision dans la vue de la caméra de sécurité est vide.

C'est parce que lorsque nous rendons la scène sur la Render Target, notre écran est vide.

Effet Inception

Pour voir la vue de la caméra de sécurité à l'intérieur de l'écran de la télé de l'écran de la télé 🤯, nous devons :

  • Créer une autre cible de rendu (nous allons la nommer bufferRenderTarget)
  • Rendre la scène dedans
  • L'attacher au material de l'écran de la télé
  • Rendre la scène au cornerRenderTarget
  • L'attacher au material de la télé
// ...
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;
  });
  // ...
};

Ça peut sembler effrayant, mais c'est en fait assez simple. Il suffit de penser à ce qui se passe à chaque étape.

Maintenant, notre scène est rendue à l'infini à l'intérieur de l'écran de la télé !

Même si c'est l'effet le plus réaliste que nous pouvons créer, ce n'est pas le plus créatif. Supprimons l'effet inception et affichons Bob l'éponge à l'écran à la place :

End of lesson preview

To get access to the entire lesson, you need to purchase the course.