Cibles de rendu

Une cible de rendu dans three.js est essentiellement une texture sur laquelle vous pouvez effectuer un rendu. Une fois le rendu effectué, vous pouvez utiliser cette texture comme n'importe quelle autre texture.

Faisons un exemple simple. Nous allons commencer par un exemple tiré de l'article sur la responsivité.

Rendre sur une cible de rendu est presque exactement la même chose qu'un rendu normal. D'abord, nous créons un WebGLRenderTarget.

const rtWidth = 512;
const rtHeight = 512;
const renderTarget = new THREE.WebGLRenderTarget(rtWidth, rtHeight);

Ensuite, nous avons besoin d'une Camera et d'une Scene

const rtFov = 75;
const rtAspect = rtWidth / rtHeight;
const rtNear = 0.1;
const rtFar = 5;
const rtCamera = new THREE.PerspectiveCamera(rtFov, rtAspect, rtNear, rtFar);
rtCamera.position.z = 2;

const rtScene = new THREE.Scene();
rtScene.background = new THREE.Color('red');

Notez que nous avons défini l'aspect sur celui de la cible de rendu, et non sur celui du canvas. Le bon aspect à utiliser dépend de ce pour quoi nous rendons. Dans ce cas, nous utiliserons la texture de la cible de rendu sur la face d'un cube. Puisque les faces de du cube sont carrées, nous voulons un aspect de 1.0.

Nous remplissons la scène. Dans ce cas, nous utilisons la lumière et les 3 cubes de l'article précédent.

{
  const color = 0xFFFFFF;
  const intensity = 1;
  const light = new THREE.DirectionalLight(color, intensity);
  light.position.set(-1, 2, 4);
*  rtScene.add(light);
}

const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);

function makeInstance(geometry, color, x) {
  const material = new THREE.MeshPhongMaterial({color});

  const cube = new THREE.Mesh(geometry, material);
*  rtScene.add(cube);

  cube.position.x = x;

  return cube;
}

*const rtCubes = [
  makeInstance(geometry, 0x44aa88,  0),
  makeInstance(geometry, 0x8844aa, -2),
  makeInstance(geometry, 0xaa8844,  2),
];

La Scene et la Camera de l'article précédent sont toujours là. Nous les utiliserons pour rendre sur le canvas. Il nous suffit d'ajouter des éléments à rendre.

Ajoutons un cube qui utilise la texture de la cible de rendu.

const material = new THREE.MeshPhongMaterial({
  map: renderTarget.texture,
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

Maintenant, au moment du rendu, nous rendons d'abord la scène de la cible de rendu sur la cible de rendu.

function render(time) {
  time *= 0.001;

  ...

  // faire tourner tous les cubes dans la scène de la cible de rendu
  rtCubes.forEach((cube, ndx) => {
    const speed = 1 + ndx * .1;
    const rot = time * speed;
    cube.rotation.x = rot;
    cube.rotation.y = rot;
  });

  // dessiner la scène de la cible de rendu sur la cible de rendu
  renderer.setRenderTarget(renderTarget);
  renderer.render(rtScene, rtCamera);
  renderer.setRenderTarget(null);

Ensuite, nous rendons la scène avec le cube unique qui utilise la texture de la cible de rendu sur le canvas.

  // faire tourner le cube dans la scène
  cube.rotation.x = time;
  cube.rotation.y = time * 1.1;

  // rendre la scène sur le canvas
  renderer.render(scene, camera);

And voilà

Le cube est rouge car nous avons défini le background de la rtScene sur rouge, de sorte que la texture de la cible de rendu est effacée en rouge.

Les cibles de rendu sont utilisées pour toutes sortes de choses. Les ombres utilisent des cibles de rendu. La sélection (picking) peut utiliser une cible de rendu. Divers types d'effets de post-traitement nécessitent des cibles de rendu. Rendre un rétroviseur dans une voiture ou une vue en direct sur un moniteur à l'intérieur d'une scène 3D pourrait utiliser une cible de rendu.

Quelques notes sur l'utilisation de WebGLRenderTarget.

  • Par défaut, WebGLRenderTarget crée 2 textures. Une texture de couleur et une texture de profondeur/stencil. Si vous n'avez pas besoin des textures de profondeur ou de stencil, vous pouvez demander à ne pas les créer en passant des options. Exemple :

      const rt = new THREE.WebGLRenderTarget(width, height, {
        depthBuffer: false,
        stencilBuffer: false,
      });
    
  • Vous pourriez avoir besoin de changer la taille d'une cible de rendu

    Dans l'exemple ci-dessus, nous créons une cible de rendu de taille fixe, 512x512. Pour des choses comme le post-traitement, vous devez généralement créer une cible de rendu de la même taille que votre canvas. Dans notre code, cela signifierait que lorsque nous changeons la taille du canvas, nous mettons également à jour la taille de la cible de rendu et la caméra que nous utilisons lors du rendu sur la cible de rendu. Exemple :

    function render(time) {
      time *= 0.001;
    
      if (resizeRendererToDisplaySize(renderer)) {
        const canvas = renderer.domElement;
        camera.aspect = canvas.clientWidth / canvas.clientHeight;
        camera.updateProjectionMatrix();
    
    +    renderTarget.setSize(canvas.width, canvas.height);
    +    rtCamera.aspect = camera.aspect;
    +    rtCamera.updateProjectionMatrix();
    }