Le brouillard

Cet article fait partie d'une série consacrée à Three.js dont le premier article s'intitule Principes de base. Si vous ne l'avez pas encore lu, vous devriez commencer par lui. Si, également, vous n'avez pas lu l'article concernant les caméras, lisez-le avant de poursuivre.

Le brouillard dans un moteur 3D est généralement un moyen d'atténuer les couleurs de la scène vers une couleur désirée en fonction de la distance par rapport à la caméra. Dans Three.js, vous pouvez ajouter du brouillard en créant un objet Fog ou FogExp2 et en le définissant sur la propriété fog de votre scène.

Fog permet de définir les paramètres near et far qui sont les distances par rapport à la caméra. Tout ce qui se trouve entre la caméra et near n'est pas affecté par le brouillard. Ce qui est au-delà de far est complètement dans le brouillard. Ce qui se trouve entre les deux, est interpolé entre la couleur du matériau et la couleur du brouillard.

Il y a aussi FogExp2 qui croît de façon exponentielle en fonction de la distance de la caméra.

Pour utiliser Fog, suivez cet exemple :

const scene = new THREE.Scene();
{
  const color = 0xFFFFFF;  // white
  const near = 10;
  const far = 100;
  scene.fog = new THREE.Fog(color, near, far);
}

Pour utiliser FogExp2, suivez cet exemple :

const scene = new THREE.Scene();
{
  const color = 0xFFFFFF;
  const density = 0.1;
  scene.fog = new THREE.FogExp2(color, density);
}

FogExp2 est le plus proche de la réalité, mais Fog est le plus souvent utilisé car il permet de choisir un endroit où appliquer le brouillard, afin de vous permettre d'afficher une scène claire jusqu'à une certaine distance, puis de passer à une autre couleur au-delà de cette distance.

THREE.Fog
THREE.FogExp2

Il est important de noter que le brouillard s'applique aux objets rendus lors du calcul sur chaque pixel de la couleur des objets. Cela signifie que si vous voulez que votre scène s'estompe avec une certaine couleur, vous devez définir le brouillard ainsi que la couleur d'arrière-plan avec la même couleur. La couleur d'arrière-plan est définie à l'aide de la propriété scene.background. Pour choisir une couleur d'arrière-plan, vous lui attachez une THREE.Color. Comme ceci :

scene.background = new THREE.Color('#F00');  // red
Brouillard bleu, arrière-plan rouge
Brouillard bleu, arrière-plan bleu

Voici l'un de nos exemples précédents mais avec du brouillard activé. L'unique ajout se fait juste après avoir configuré la scène : nous ajoutons le brouillard et définissons la couleur d'arrière-plan de la scène.

const scene = new THREE.Scene();

+{
+  const near = 1;
+  const far = 2;
+  const color = 'lightblue';
+  scene.fog = new THREE.Fog(color, near, far);
+  scene.background = new THREE.Color(color);
+}

Dans l'exemple ci-dessous, le near de la caméra est à 0,1 et le far à 5. La position z de la caméra est à 2. Les cubes mesurent 1 unité de large et à Z = 0. Les réglages du brouillard, near = 1 et far = 2. Ainsi, les cubes s'estompent juste autour de leur centre.

Mettons à jour notre lil-gui pour jouer avec le brouillard. Lil-gui prend un objet et une propriété et crée automatiquement une interface de contrôle pour cette propriété. Nous pourrions simplement le laisser modifier les propriétés near et far du brouillard, mais il est impossible que near soit supérieur à far. Assurons-nous de cela.

// On utilise cette classe pour passer à lil-gui.
// Quand lil-gui modifie near ou far :
//  - near n'est jamais strictement supérieur à far
//  - far n'est jamais strictement inférieur à near
class FogGUIHelper {
  constructor(fog) {
    this.fog = fog;
  }
  get near() {
    return this.fog.near;
  }
  set near(v) {
    this.fog.near = v;
    this.fog.far = Math.max(this.fog.far, v);
  }
  get far() {
    return this.fog.far;
  }
  set far(v) {
    this.fog.far = v;
    this.fog.near = Math.min(this.fog.near, v);
  }
}

On peut l'ajouter comme ceci :

{
  const near = 1;
  const far = 2;
  const color = 'lightblue';
  scene.fog = new THREE.Fog(color, near, far);
  scene.background = new THREE.Color(color);
+
+  const fogGUIHelper = new FogGUIHelper(scene.fog);
+  gui.add(fogGUIHelper, 'near', near, far).listen();
+  gui.add(fogGUIHelper, 'far', near, far).listen();
}

Les paramètres near et far définissent les valeurs minimales et maximales pour ajuster le brouillard. Ils sont définis lors de la configuration de la caméra.

Le .listen() à la fin des 2 lignes, dit à lil-gui d'écouter les changements. Ainsi, que nous changions near ou far, lil-gui mettra automatiquement à jour les deux propriétés pour nous.

Il peut également être agréable de pouvoir changer la couleur du brouillard, mais comme mentionné ci-dessus, nous devons synchroniser la couleur du brouillard et la couleur de l'arrière-plan. Ajoutons donc une autre propriété virtuelle à notre helper qui définira les deux couleurs lorsque lil-gui la manipule.

lil-gui peut manipuler les couleurs de 4 façons différentes : - Sous la forme d'une chaîne hexadécimale à 6 chiffres (ex : #112233); - Sous la forme HSL (ex : {h: 60, s: 1, v: }); - En tant que tableau RGB (ex : [255, 128, 64]); - Ou finalement, comme un tableau RGBA (ex : [127, 200, 75, 0.3]).

Il est plus simple d'utiliser la première solution, la version chaîne hexadécimale, ainsi lil-gui ne manipule qu'une seule valeur. Heureusement, THREE.Color a une méthode pour cela : getHexString qui permet d'obtenir une telle chaîne, il suffit juste d'ajouter un '#' au début.

/// On utilise cette classe pour passer à lil-gui
// Quand il manipule near ou far
// near n'est jamais > far et far n'est jamais < near
+// Aussi, lorsque lil-gui manipule la couleur, nous allons
+// mettre à jour les couleurs du brouillard et de l'arrière-plan.
class FogGUIHelper {
*  constructor(fog, backgroundColor) {
    this.fog = fog;
+    this.backgroundColor = backgroundColor;
  }
  get near() {
    return this.fog.near;
  }
  set near(v) {
    this.fog.near = v;
    this.fog.far = Math.max(this.fog.far, v);
  }
  get far() {
    return this.fog.far;
  }
  set far(v) {
    this.fog.far = v;
    this.fog.near = Math.min(this.fog.near, v);
  }
+  get color() {
+    return `#${this.fog.color.getHexString()}`;
+  }
+  set color(hexString) {
+    this.fog.color.set(hexString);
+    this.backgroundColor.set(hexString);
+  }
}

Ensuite, nous appelons gui.addColor pour ajouter une couleur à notre propriété virtuelle :

{
  const near = 1;
  const far = 2;
  const color = 'lightblue';
  scene.fog = new THREE.Fog(color, near, far);
  scene.background = new THREE.Color(color);

*  const fogGUIHelper = new FogGUIHelper(scene.fog, scene.background);
  gui.add(fogGUIHelper, 'near', near, far).listen();
  gui.add(fogGUIHelper, 'far', near, far).listen();
+  gui.addColor(fogGUIHelper, 'color');
}

Vous pouvez voir qu'un réglage near à 1.9 et far à 2.0 donne une transition très nette entre non embué et complètement dans le brouillard. near = 1.1 et far = 2.9 devrait être la meilleure configuration étant donné que nos cubes tournent à 2 unités de la caméra.

Une dernière chose ! Il existe une propriété fog pour savoir si les objets rendus avec ce matériau sont affectés ou non par le brouillard. La valeur par défaut est true pour la plupart des matériaux. Voici deux exemples illustrant cette volonté de désactiver le brouillard : imaginez que vous créez un simulateur de véhicule 3D avec une vue depuis le siège du conducteur (cockpit). Vous ne voulez pas qu'il y ait de brouillard à l'intérieur du véhicule. Prenons un second exemple : une maison avec un épais brouillard à l'extérieur. Disons que pour commencer, le brouillard est réglé pour commencer à 2 mètres (near = 2) et être total à 4 mètres (far = 4). Les pièces et la maison faisant plus de 4 mètres, il vous faudra donc définir les matériaux utilisés à l'intérieur de la maison pour qu'il n'y ait pas de brouillard, sinon, cela donnerait l'aspect non désiré suivant :

fog à true sur tous les objets.

Remarquez que les murs et le plafond au fond de la pièce sont dans le brouillard. En désactivant le brouillard sur les matériaux de la maison, on résout ce problème.

fog à true uniquement sur les matériaux extérieurs de la maison.