# Arrière-plans et Skyboxes

La plupart des articles ici utilisent une couleur unie pour l'arrière-plan.

Ajouter un arrière-plan statique peut être aussi simple que de définir du CSS. En prenant un exemple de l'article sur comment rendre THREE.js responsive nous n'avons besoin de changer que 2 choses.

Nous devons ajouter du CSS à notre canvas pour définir son arrière-plan comme une image.

    <style>
    body {
        margin: 0;
    }
    #c {
        width: 100%;
        height: 100%;
        display: block;
    +    background: url(resources/images/daikanyama.jpg) no-repeat center center;
    +    background-size: cover;
    }
    </style>

et nous devons dire au `WebGLRenderer` d'utiliser `alpha` pour que les endroits où nous ne dessinons rien soient transparents.

    function main() {
      const canvas = document.querySelector('#c');
    -  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
    +  const renderer = new THREE.WebGLRenderer({
    +    antialias: true,
    +    canvas,
    +    alpha: true,
    +  });

Et nous obtenons un arrière-plan.

cliquez ici pour ouvrir dans une fenêtre séparée

Si nous voulons que l'arrière-plan puisse être affecté par des effets de post-traitement alors nous devons dessiner l'arrière-plan en utilisant THREE.js.

THREE.js rend cela quelque peu simple. Nous pouvons simplement définir l'arrière-plan de la scène sur une texture.

    const loader = new THREE.TextureLoader();
    const bgTexture = loader.load('resources/images/daikanyama.jpg');
    bgTexture.colorSpace = THREE.SRGBColorSpace;
    scene.background = bgTexture;

ce qui nous donne

cliquez ici pour ouvrir dans une fenêtre séparée

Cela nous donne une image de fond, mais elle est étirée pour s'adapter à l'écran.

Nous pouvons résoudre ce problème en définissant les propriétés `repeat` et `offset` de la texture pour n'afficher qu'une partie de l'image.

    function render(time) {

       ...

    +  // Définir les propriétés repeat et offset de la texture de fond
    +  // pour maintenir l'aspect correct de l'image.
    +  // Note : l'image peut ne pas avoir encore été chargée.
    +  const canvasAspect = canvas.clientWidth / canvas.clientHeight;
    +  const imageAspect = bgTexture.image ? bgTexture.image.width / bgTexture.image.height : 1;
    +  const aspect = imageAspect / canvasAspect;
    +
    +  bgTexture.offset.x = aspect > 1 ? (1 - 1 / aspect) / 2 : 0;
    +  bgTexture.repeat.x = aspect > 1 ? 1 / aspect : 1;
    +
    +  bgTexture.offset.y = aspect > 1 ? 0 : (1 - aspect) / 2;
    +  bgTexture.repeat.y = aspect > 1 ? 1 : aspect;

      ...

      renderer.render(scene, camera);

      requestAnimationFrame(render);
    }

et maintenant THREE.js dessine l'arrière-plan. Il n'y a pas de différence visible avec la version CSS en haut, mais maintenant si nous utilisions un effet de post-traitement l'arrière-plan serait également affecté.

cliquez ici pour ouvrir dans une fenêtre séparée

Bien sûr, un arrière-plan statique n'est généralement pas ce que l'on souhaite dans une scène 3D. Au lieu de cela, nous voulons généralement une sorte de skybox. Une skybox est exactement cela, une boîte avec le ciel dessiné dessus. Nous plaçons la caméra à l'intérieur de la boîte et on dirait qu'il y a un ciel en arrière-plan.

La manière la plus courante d'implémenter une skybox est de créer un cube, d'y appliquer une texture, et de le dessiner depuis l'intérieur. Sur chaque côté du cube, placez une texture (en utilisant les coordonnées de texture) qui ressemble à une image de l'horizon. Il est également fréquent d'utiliser une sphère ou un dôme céleste avec une texture dessinée dessus. Vous pouvez probablement le comprendre par vous-même. Il suffit de faire un cube ou une sphère, appliquer une texture, le marquer comme `THREE.BackSide` pour que nous rendions l'intérieur au lieu de l'extérieur, et soit de le mettre directement dans votre scène comme ci-dessus, soit de créer 2 scènes : une spéciale pour dessiner la skybox/sphère/dôme et la scène normale pour dessiner tout le reste. Vous utiliseriez votre `PerspectiveCamera` normale pour dessiner. Pas besoin de la `OrthographicCamera`.

Une autre solution consiste à utiliser une Cubemap. Une Cubemap est un type de texture spécial qui a 6 côtés, les côtés d'un cube. Au lieu d'utiliser des coordonnées de texture standard, elle utilise une direction depuis le centre pointant vers l'extérieur pour décider où obtenir une couleur.

Voici les 6 images d'une cubemap provenant du musée d'histoire de l'ordinateur à Mountain View, Californie.

Pour les utiliser, nous utilisons le `CubeTextureLoader` pour les charger, puis nous l'utilisons comme arrière-plan de la scène.

    {
      const loader = new THREE.CubeTextureLoader();
      const texture = loader.load([
        'resources/images/cubemaps/computer-history-museum/pos-x.jpg',
        'resources/images/cubemaps/computer-history-museum/neg-x.jpg',
        'resources/images/cubemaps/computer-history-museum/pos-y.jpg',
        'resources/images/cubemaps/computer-history-museum/neg-y.jpg',
        'resources/images/cubemaps/computer-history-museum/pos-z.jpg',
        'resources/images/cubemaps/computer-history-museum/neg-z.jpg',
      ]);
      scene.background = texture;
    }

Au moment du rendu, nous n'avons pas besoin d'ajuster la texture comme nous l'avons fait ci-dessus

    function render(time) {

       ...

    -  // Définir les propriétés repeat et offset de la texture de fond
    -  // pour maintenir l'aspect correct de l'image.
    -  // Note : l'image peut ne pas avoir encore été chargée.
    -  const canvasAspect = canvas.clientWidth / canvas.clientHeight;
    -  const imageAspect = bgTexture.image ? bgTexture.image.width / bgTexture.image.height : 1;
    -  const aspect = imageAspect / canvasAspect;
    -
    -  bgTexture.offset.x = aspect > 1 ? (1 - 1 / aspect) / 2 : 0;
    -  bgTexture.repeat.x = aspect > 1 ? 1 / aspect : 1;
    -
    -  bgTexture.offset.y = aspect > 1 ? 0 : (1 - aspect) / 2;
    -  bgTexture.repeat.y = aspect > 1 ? 1 : aspect;

      ...

      renderer.render(scene, camera);

      requestAnimationFrame(render);
    }

Ajoutons des contrôles pour pouvoir faire pivoter la caméra.

    import {OrbitControls} from 'three/addons/controls/OrbitControls.js';

    const fov = 75;
    const aspect = 2;  // the canvas default
    const near = 0.1;
    -const far = 5;
    +const far = 100;
    const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
    -camera.position.z = 2;
    +camera.position.z = 3;

    +const controls = new OrbitControls(camera, canvas);
    +controls.target.set(0, 0, 0);
    +controls.update();

et essayez-le. Faites glisser l'exemple pour faire pivoter la caméra et voir la cubemap nous entourer.

cliquez ici pour ouvrir dans une fenêtre séparée

Une autre option consiste à utiliser une carte équirectangulaire. C'est le type d'image qu'une caméra 360 prend.

En voici une que j'ai trouvée sur ce site.

    {
    -  const loader = new THREE.CubeTextureLoader();
    -  const texture = loader.load([
    -    'resources/images/cubemaps/computer-history-museum/pos-x.jpg',
    -    'resources/images/cubemaps/computer-history-museum/neg-x.jpg',
    -    'resources/images/cubemaps/computer-history-museum/pos-y.jpg',
    -    'resources/images/cubemaps/computer-history-museum/neg-y.jpg',
    -    'resources/images/cubemaps/computer-history-museum/pos-z.jpg',
    -    'resources/images/cubemaps/computer-history-museum/neg-z.jpg',
    -  ]);
    -  scene.background = texture;
    +  const loader = new THREE.TextureLoader();
    +  const texture = loader.load(
    +    'resources/images/equirectangularmaps/tears_of_steel_bridge_2k.jpg',
    +    () => {
    +      texture.mapping = THREE.EquirectangularReflectionMapping;
    +      texture.colorSpace = THREE.SRGBColorSpace;
    +      scene.background = texture;
    +    });
    }

Et c'est tout ce qu'il y a à faire.

cliquez ici pour ouvrir dans une fenêtre séparée

Plutôt que de le faire au moment du chargement, vous pouvez également convertir une image équirectangulaire en cubemap au préalable. Voici un site qui le fera pour vous.