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>