Aller au contenu

Lumières, ombres et réglages

Éclairer un matériau réaliste, projeter des ombres, tourner autour de la scène avec OrbitControls et régler les valeurs en direct avec lil-gui.

main.js
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import GUI from "lil-gui";

const scene = new THREE.Scene();
scene.background = new THREE.Color("#038a03");

const camera = new THREE.PerspectiveCamera(
  45,
  window.innerWidth / window.innerHeight,
  0.1,
  100,
);
camera.position.set(4, 3, 6);

const cube = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshStandardMaterial({
    color: "#ff0000",
  }),
);
cube.position.y = 1;
cube.castShadow = true;

const floor = new THREE.Mesh(
  new THREE.PlaneGeometry(10, 10),
  new THREE.MeshStandardMaterial({ color: "#ffffff" }),
);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(cube, floor);

const ambientLight = new THREE.AmbientLight("#ffffff", 0.4);
scene.add(ambientLight);

const sunLight = new THREE.DirectionalLight("#ffffff", 2);
sunLight.position.set(3, 5, 2);
sunLight.castShadow = true;
scene.add(sunLight);

const sunHelper = new THREE.DirectionalLightHelper(sunLight);
scene.add(sunHelper, new THREE.AxesHelper(2));

const renderer = new THREE.WebGLRenderer({
  antialias: true,
});
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

const gui = new GUI();
gui.add(sunLight, "intensity", 0, 5).name("soleil");
gui.addColor(cube.material, "color").name("couleur du cube");

renderer.setAnimationLoop(() => {
  controls.update();
  renderer.render(scene, camera);
});

window.addEventListener("resize", () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(window.innerWidth, window.innerHeight);
});

Ce qu’on ajoute

Par rapport à la « Première scène », cette démo apporte quatre nouveautés : un matériau qui réagit à la lumière, deux lumières, des ombres, et deux outils de travail (OrbitControls pour tourner autour de la scène, lil-gui pour régler des valeurs sans recharger). Installe d’abord lil-gui, OrbitControls est déjà livré avec three :

Terminal
npm install lil-gui
main.js
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import GUI from "lil-gui";

Un matériau qui a besoin de lumière

main.js
const cube = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshStandardMaterial({
    color: "#ff0000",
  }),
);
cube.position.y = 1;

MeshStandardMaterial imite un matériau réel : il est sombre sans lumière, et il reçoit les ombres. Le sol est un PlaneGeometry : un plan est vertical à sa création, on le couche en le tournant d’un quart de tour (-Math.PI / 2 radians) autour de l’axe x.

Si tout est noir, c’est presque toujours qu’il manque une lumière dans la scène.

Deux lumières

main.js
const ambientLight = new THREE.AmbientLight("#ffffff", 0.4);
scene.add(ambientLight);

const sunLight = new THREE.DirectionalLight("#ffffff", 2);
sunLight.position.set(3, 5, 2);
scene.add(sunLight);
  • AmbientLight éclaire tout, partout, de la même façon : elle débouche les zones d’ombre mais ne dessine aucun relief.
  • DirectionalLight imite le soleil : des rayons parallèles qui viennent de sa position et vont vers le centre de la scène. C’est elle qui donne le relief.

Le deuxième nombre est l’intensité. Pour voir où sont les choses, deux aides se dessinent dans la scène : DirectionalLightHelper montre la lumière, AxesHelper montre les axes (x en rouge, y en vert, z en bleu).

Les ombres

Les ombres coûtent cher à calculer : Three.js les coupe par défaut. Il faut les activer à quatre endroits, sinon rien ne s’affiche :

  1. renderer.shadowMap.enabled = true sur le rendu ;
  2. sunLight.castShadow = true sur la lumière qui projette ;
  3. cube.castShadow = true sur chaque objet qui fait de l’ombre ;
  4. floor.receiveShadow = true sur chaque objet qui la reçoit.

Seules certaines lumières projettent des ombres (DirectionalLight, SpotLight, PointLight). AmbientLight n’en fait jamais.

Tourner autour de la scène

main.js
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

renderer.setAnimationLoop(() => {
  controls.update();
  renderer.render(scene, camera);
});

Clic gauche pour tourner, molette pour zoomer, clic droit pour se déplacer. enableDamping ajoute de l’inertie : pour qu’elle marche, il faut appeler controls.update() à chaque image.

Régler en direct avec lil-gui

main.js
const gui = new GUI();
gui.add(sunLight, "intensity", 0, 5).name("soleil");
gui.addColor(cube.material, "color").name("couleur du cube");

gui.add(objet, "propriété", min, max) crée un curseur qui modifie directement la propriété de l’objet. addColor crée un sélecteur de couleur. name() change le libellé affiché. Le panneau apparaît en haut à droite : cherche les bonnes valeurs à la main, puis recopie-les dans ton code.

Pour aller plus loin

  • Ajoute gui.add(sunLight.position, "x", -5, 5) pour déplacer le soleil et voir l’ombre bouger.
  • Remplace DirectionalLight par une PointLight (une ampoule) et compare.
  • La documentation de lil-gui montre les autres types de réglages (cases à cocher, listes, boutons).

Mise à jour : ven. 30 oct. à 17:05