Premiers Pas avec Three.js pour la Visualisation 3D

Three.js est une biblitohèque JavaScript puissante et populaire, exploitant WebGL, qui simplifie la création et l'affichage de graphiques 3D directement dans le navigateur. Cet article guide les développeurs à travers les étapes initiales pour configurer un environnement Three.js, créer une scène simple, y ajouter des objets et interagir avec eux.

Mise en place de l'environnement

Installation du package

Pour commencer, installez la bibliothèque Three.js dans votre projet. La commande suivante cible une version spécifique pour assurer la compatibilité:

npm install three@0.137.5

Configuration du conteneur HTML

La visualisation 3D de Three.js est généralement rendue sur un élément <canvas>, lequel est ensuite inséré dans le DOM. Pour nos exemples, nous utiliserons un simple conteneur <div> comme point d'ancrage:

<template>
 <div class="visualisation-3d" ref="conteneurRef"></div>
</template>

Création de votre première scène 3D

Une scène Three.js est composée de plusieurs éléments fondamentaux : une scène pour organiser les objets, une caméra pour les observer, et un moteur de rendu pour les afficher.

Les composants fondamentaux : Scène, Caméra, Rendu

  • Scène (THREE.Scene) : C'est le conteneur principal de tous les objets, lumières et caméras.
  • Caméra (THREE.PerspectiveCamera) : Définit le point de vue de l'observateur. Une caméra perspective imite la vision humaine, avec les objets éloignés apparaissant plus petits.
  • Moteur de rendu (THREE.WebGLRenderer) : Traite la scène et la caméra pour créer l'image finale sur le <canvas>.

Ajout d'objets : Géométrie et Matériaux

Pour afficher quelque chose dans la scène, il faut un objet. Chaque objet 3D est défini par une géométrie (sa forme) et un matériau (son apparence). L'ensemble est combiné dans un THREE.Mesh.

Voici comment initialiser une scène avec un cube simple:

<script setup>
import * as THREE from 'three';
import { ref, onMounted } from 'vue'; // Pour l'exemple Vue, ou useRef pour React

const conteneurRef = ref(null); // Ref pour le conteneur div

onMounted(() => {
   if (!conteneurRef.value) return;

   const largeurCanvas = conteneurRef.value.clientWidth;
   const hauteurCanvas = conteneurRef.value.clientHeight;

   // 1. Initialisation de la scène
   const scene3D = new THREE.Scene();

   // 2. Configuration de la caméra perspective
   // (FOV, Aspect Ratio, Near, Far)
   const cameraPrincipale = new THREE.PerspectiveCamera(65, largeurCanvas / hauteurCanvas, 0.5, 1200);
   cameraPrincipale.position.set(0, 0, 8); // Positionner la caméra
   scene3D.add(cameraPrincipale);

   // 3. Création d'un objet simple (un cube)
   const geometrieCube = new THREE.BoxGeometry(1.2, 1.2, 1.2); // Dimensions du cube
   const materiauCube = new THREE.MeshBasicMaterial({ color: 0x00ffcc }); // Matériau de base, couleur turquoise
   const objetCube = new THREE.Mesh(geometrieCube, materiauCube);
   scene3D.add(objetCube); // Ajouter le cube à la scène

   // 4. Initialisation du moteur de rendu WebGL
   const renduWebgl = new THREE.WebGLRenderer();
   renduWebgl.setSize(largeurCanvas, hauteurCanvas); // Définir la taille du rendu
   conteneurRef.value.appendChild(renduWebgl.domElement); // Ajouter le canvas au DOM

   // 5. Première image rendue
   renduWebgl.render(scene3D, cameraPrincipale);
});
</script>

Interaction avec la scène : Les contrôles orbitaux

Pour naviguer intuitivement dans votre scène 3D, les OrbitControls sont essentiels. Ils permettent à l'utilisateur de faire pivoter, zoomer et déplacer la caméra autour d'un point d'intérêt à l'aide de la souris ou des gestes tactiles.

Intégration des contrôles

D'abord, importez les contrôles:

import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

Boucle de rendu

Les contrôles orbitaux nécessitent une mise à jour continue. Cela se fait via une boucle d'animation qui redessine la scène à chaque image, garantissant une interaction fluide.

// ... (suite du code précédent)

   // Activation des contrôles orbitaux pour la navigation
   const controleurOrbital = new OrbitControls(cameraPrincipale, renduWebgl.domElement);

   // Boucle d'animation pour les interactions
   const animerScene = () => {
       controleurOrbital.update(); // Mettre à jour les contrôles à chaque frame
       renduWebgl.render(scene3D, cameraPrincipale);
       requestAnimationFrame(animerScene); // Appeler la fonction à la prochaine image
   };
   animerScene(); // Démarrer la boucle d'animation

Aide à la visualisation : Les assistants d'axes

Pour faciliter le développement et la compréhension de l'orientation spatiale, il est utile d'ajouter un assistant d'axes. Le THREE.AxesHelper dessine trois lignes représentant les axes X (rouge), Y (vert) et Z (bleu).

// ... (suite du code précédent)

   // Ajout d'un assistant visuel pour les axes (X, Y, Z)
   const aideAxes = new THREE.AxesHelper(7); // Taille des axes
   scene3D.add(aideAxes); // Ajouter l'assistant à la scène

Exemple complet avec React

Voici un exemple complet intégrant tous les concepts abordés dans une application React, affichant un objet avec des lumières et des contrôles de caméra.

import React, { useRef, useEffect } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import './App.css'; // Pour les styles du conteneur

function Visualisation3DReact() {
 const conteneurSceneRef = useRef(null);

 useEffect(() => {
   if (!conteneurSceneRef.current) return;

   let sceneInstance, cameraInstance, rendererInstance, controlsInstance;
   const initScene = () => {
     const largeur = conteneurSceneRef.current.clientWidth;
     const hauteur = conteneurSceneRef.current.clientHeight;

     // Configuration de la scène
     sceneInstance = new THREE.Scene();
     sceneInstance.background = new THREE.Color(0xabcdef); // Couleur de fond

     // Configuration de la caméra
     cameraInstance = new THREE.PerspectiveCamera(55, largeur / hauteur, 0.1, 1500);
     cameraInstance.position.set(50, 60, 150); // Position initiale de la caméra
     cameraInstance.lookAt(0, 0, 0);
     sceneInstance.add(cameraInstance);

     // Ajout d'une géométrie et d'un matériau
     const geometrieCapsule = new THREE.CapsuleGeometry(15, 60, 4, 8); // Une capsule
     const materiauPhong = new THREE.MeshPhongMaterial({
       color: 0x98f5ff, // Une couleur bleu clair
       specular: 0xcccccc,
       shininess: 30,
     });
     const objetMesh = new THREE.Mesh(geometrieCapsule, materiauPhong);
     objetMesh.position.set(0, 30, 0); // Positionner la capsule légèrement au-dessus du centre
     sceneInstance.add(objetMesh);

     // Ajout de lumières
     const lumiereDirectionnelle = new THREE.DirectionalLight(0xffffff, 0.7);
     lumiereDirectionnelle.position.set(100, 150, 100);
     sceneInstance.add(lumiereDirectionnelle);

     const lumiereAmbiante = new THREE.AmbientLight(0xffffff, 0.3);
     sceneInstance.add(lumiereAmbiante);

     // Initialisation du renderer
     rendererInstance = new THREE.WebGLRenderer({ antialias: true });
     rendererInstance.setSize(largeur, hauteur);
     conteneurSceneRef.current.appendChild(rendererInstance.domElement);

     // Contrôles orbitaux
     controlsInstance = new OrbitControls(cameraInstance, rendererInstance.domElement);
     controlsInstance.enableDamping = true; // Pour un mouvement plus fluide
     controlsInstance.dampingFactor = 0.05;

     // Assistant d'axes
     const helperAxes = new THREE.AxesHelper(120);
     sceneInstance.add(helperAxes);

     // Boucle d'animation
     const clock = new THREE.Clock();
     const animate = () => {
       requestAnimationFrame(animate);
       controlsInstance.update(clock.getDelta()); // Mise à jour des contrôles
       rendererInstance.render(sceneInstance, cameraInstance);
     };
     animate();
   };

   initScene();

   // Nettoyage à la suppression du composant
   return () => {
     if (rendererInstance) {
       rendererInstance.dispose();
       conteneurSceneRef.current?.removeChild(rendererInstance.domElement);
     }
     if (controlsInstance) {
       controlsInstance.dispose();
     }
   };
 }, []); // Exécuter une seule fois au montage

 return (
   <div id='scene-container-react' ref={conteneurSceneRef}></div>
 );
}

export default Visualisation3DReact;

Exemple complet avec Vue.js

Voici l'implémentation de ces mêmes concpets dans un composant Vue 3 utilisant l'API de Composition.

<template>
 <div class="scene-vue-container" ref="conteneurVue"></div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

const conteneurVue = ref(null);
let scene, camera, renderer, controls, animationFrameId;

onMounted(() => {
 if (!conteneurVue.value) return;

 const width = conteneurVue.value.clientWidth;
 const height = conteneurVue.value.clientHeight;

 // Initialisation de la scène
 scene = new THREE.Scene();
 scene.background = new THREE.Color(0xddddff); // Un fond bleu très clair

 // Caméra
 camera = new THREE.PerspectiveCamera(40, width / height, 1, 1200);
 camera.position.set(80, 100, 180); // Positionnement distinct de la caméra
 camera.lookAt(0, 0, 0);
 scene.add(camera);

 // Objet : Un Cône
 const geometrieCone = new THREE.ConeGeometry(25, 80, 32); // Rayon, hauteur, segments radiaux
 const materiauStandard = new THREE.MeshStandardMaterial({
   color: 0xffa07a, // Couleur corail
   roughness: 0.6,
   metalness: 0.2,
 });
 const objetCone = new THREE.Mesh(geometrieCone, materiauStandard);
 objetCone.position.y = 40; // Déplacer le cône au-dessus du plan XY
 scene.add(objetCone);

 // Lumières
 const lumiereSpot = new THREE.SpotLight(0xffffff, 0.8, 0, Math.PI / 4, 0.5);
 lumiereSpot.position.set(60, 120, 60);
 lumiereSpot.castShadow = true;
 scene.add(lumiereSpot);

 const lumiereAmbiente = new THREE.AmbientLight(0x404040, 0.6); // Lumière ambiante douce
 scene.add(lumiereAmbiente);

 // Rendu
 renderer = new THREE.WebGLRenderer({ antialias: true });
 renderer.setSize(width, height);
 renderer.shadowMap.enabled = true; // Activer les ombres
 conteneurVue.value.appendChild(renderer.domElement);

 // Assistant d'axes
 const axesAide = new THREE.AxesHelper(150);
 scene.add(axesAide);

 // Contrôles orbitaux
 controls = new OrbitControls(camera, renderer.domElement);
 controls.enablePan = false; // Désactiver le déplacement latéral
 controls.minDistance = 50;
 controls.maxDistance = 400;

 const clock = new THREE.Clock();

 // Boucle de rendu
 const animate = () => {
   animationFrameId = requestAnimationFrame(animate);
   controls.update(clock.getDelta());
   renderer.render(scene, camera);
 };
 animate();
});

onBeforeUnmount(() => {
 if (animationFrameId) {
   cancelAnimationFrame(animationFrameId);
 }
 if (renderer) {
   renderer.dispose();
   conteneurVue.value?.removeChild(renderer.domElement);
 }
 if (controls) {
   controls.dispose();
 }
 // Libérer les ressources de la scène si nécessaire
});
</script>

<style scoped>
.scene-vue-container {
 width: 100vw;
 height: 100vh;
 background-color: #f0f8ff; /* Couleur de fond légère pour le conteneur */
}
</style>

Étiquettes: Three.js WebGL 3D JavaScript Vue.js

Publié le 8 juin à 07h27