aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Frontend & UI

Animation Three.js : créer une scène 3D vivante

Quand on parle de graphisme 3D en temps réel dans le navigateur, Three.js est la première bibliothèque qui vient à l'esprit ; dans cet article, nous allons construire la logique d'animation Three.js de zéro pour créer une scène vivante qui tourne, capte la lumière et réagit à l'interaction de l'utilisateur. L'objectif n'est pas seulement de faire tourner un cube, mais de comprendre comment fonctionne la boucle de rendu, pourquoi le temps entre les images compte et comment garder la scène performante. À la fin, vous disposerez d'un modèle de départ qui tourne dans un seul fichier HTML sans jamais perdre d'images.

Les trois piliers d'une scène Three.js

Chaque projet Three.js s'articule autour de trois objets : la Scene, la Camera et le Renderer. La scène est le conteneur qui contient tout ce qui est visible. La caméra définit l'angle sous lequel nous regardons cette scène. Le renderer utilise WebGL pour dessiner le résultat sur un <canvas>.

  • Scene : la racine de l'arbre graphique contenant objets, lumières et caméras.
  • PerspectiveCamera : une caméra qui, comme l'œil humain, rapetisse les objets lointains et donne une sensation de profondeur.
  • WebGLRenderer : s'exécute sur le GPU et redessine la scène à chaque image.

Importer la bibliothèque en tant que module est l'approche la plus propre. Les navigateurs modernes peuvent récupérer un module ES directement depuis un CDN grâce à une import map.

<script type="importmap">
{
  "imports": {
    "three": "https://unpkg.com/three@0.160.0/build/three.module.js"
  }
}
</script>

<script type="module">
import * as THREE from 'three';
</script>

Mettre en place la première scène

Créons maintenant la scène, la caméra et le renderer. Les paramètres de la caméra sont, dans l'ordre : le champ de vision (fov, en degrés), le rapport d'aspect, le plan de découpe proche et le plan de découpe lointain. Pour ajouter le renderer à la page, on place sa propriété domElement dans le DOM.

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0b1020);

const camera = new THREE.PerspectiveCamera(
  60, window.innerWidth / window.innerHeight, 0.1, 100
);
camera.position.z = 4;

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

Limiter setPixelRatio à 2 est important : sur les écrans à haute densité, brider le ratio de pixels maintient une résolution raisonnable, soulage le GPU et réduit la surchauffe sur mobile.

Objet, matériau et lumière

Dans Three.js, un objet visible (mesh) est la combinaison d'une géométrie (forme) et d'un matériau (apparence de surface). MeshStandardMaterial est un matériau basé sur la physique qui a besoin de lumière pour paraître réaliste ; c'est pourquoi l'écran reste noir tant que vous n'ajoutez pas au moins une lumière à la scène.

const geometry = new THREE.IcosahedronGeometry(1, 0);
const material = new THREE.MeshStandardMaterial({
  color: 0x4f9dde,
  roughness: 0.3,
  metalness: 0.6,
  flatShading: true,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

const keyLight = new THREE.DirectionalLight(0xffffff, 2);
keyLight.position.set(3, 4, 5);
scene.add(keyLight);
scene.add(new THREE.AmbientLight(0x404060, 1.5));

Ici, nous combinons une lumière directionnelle (telle le soleil, avec une direction nette et des ombres dures) et une lumière ambiante (égale dans toutes les directions, un remplissage qui adoucit les ombres). Ce duo offre un éclairage simple mais équilibré.

La boucle de rendu et l'animation basée sur le temps

Le cœur de l'animation est la boucle de rendu. requestAnimationFrame dit au navigateur : « appelle-moi quand tu es prêt pour le prochain rendu » ; cela se produit généralement 60 fois par seconde, en phase avec la fréquence de rafraîchissement de l'écran. L'approche naïve ajoute une valeur fixe à chaque image, mais c'est une erreur : la rotation est lente sur un écran 60 Hz et rapide sur un écran 144 Hz. La bonne méthode consiste à multiplier par le temps écoulé (delta).

const clock = new THREE.Clock();

function animate() {
  requestAnimationFrame(animate);

  const delta = clock.getDelta(); // temps écoulé en secondes
  mesh.rotation.y += delta * 0.8;  // 0,8 radian par seconde
  mesh.rotation.x += delta * 0.3;

  renderer.render(scene, camera);
}
animate();

THREE.Clock donne le temps réel écoulé entre les images. Multiplier la vitesse de rotation par cette valeur fait que l'animation tourne à la même vitesse sur chaque appareil, indépendamment de la fréquence de rafraîchissement de l'écran. C'est l'une des différences les plus visibles entre un travail professionnel et amateur.

S'adapter à la taille de la fenêtre et à l'interaction

Pour que l'image ne se déforme pas lorsque l'utilisateur redimensionne la fenêtre, nous devons mettre à jour la caméra et le renderer. Lorsque le rapport d'aspect de la caméra change, un appel à updateProjectionMatrix est obligatoire.

window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});

Pour l'interaction, la voie la plus rapide est le module officiel OrbitControls, qui permet de faire pivoter et de zoomer la scène à la souris. Il suffit de l'ajouter à l'import map et de placer controls.update() dans la boucle. Si vous voulez une sensation plus légère, vous pouvez lier la position de la souris à un léger décalage de la caméra (un effet de parallaxe).

Conseils de performance et de nettoyage

  • Mettre en pause quand l'onglet est en arrière-plan : requestAnimationFrame ralentit déjà quand la page est masquée, mais pour les scènes lourdes, arrêter complètement la boucle avec un contrôle document.hidden préserve la batterie.
  • Réutiliser géométrie et matériau : si vous avez besoin de centaines de copies d'une même forme, InstancedMesh les dessine toutes en un seul appel de rendu.
  • Attention aux fuites de mémoire : lorsque vous supprimez un objet de façon permanente, appelez geometry.dispose() et material.dispose() ; la mémoire du GPU n'est pas libérée automatiquement.
  • Utilisez les ombres avec parcimonie : les cartes d'ombres en temps réel sont coûteuses ; gardez-les désactivées si vous n'en avez pas besoin.

Questions fréquentes

Faut-il connaître WebGL pour apprendre Three.js ?

Non. Three.js masque les détails complexes des shaders et des buffers de WebGL ; pour débuter, JavaScript et les concepts 3D de base (coordonnées, vecteurs, caméras) suffisent. La connaissance de WebGL devient un atout plus tard, quand vous voudrez écrire des shaders personnalisés, mais elle n'est pas obligatoire.

Pourquoi mon animation tourne-t-elle si vite sur certains ordinateurs ?

Très probablement, vous incrémentez la rotation d'un nombre fixe. Sur les écrans à haute fréquence (120/144 Hz), la boucle s'exécute plus souvent et le mouvement s'accélère. La solution est de multiplier le mouvement par clock.getDelta(), c'est-à-dire d'utiliser une animation basée sur le temps.

Three.js fonctionne-t-il bien sur les appareils mobiles ?

Oui, mais il faut être prudent. Limitez setPixelRatio, gardez un faible nombre de polygones, évitez les effets de post-traitement lourds et limitez les ombres. Avec ces précautions, vous pouvez faire tourner des scènes 3D fluides même sur des téléphones de milieu de gamme.

Vous rêvez d'une expérience 3D vivante dans le navigateur ? Du configurateur de produit à l'arrière-plan interactif, je conçois des scènes basées sur Three.js et les concrétise en pensant à la performance. Pour parler de votre projet, contactez-moi.

Bu kategorideki tüm yazılar →

Devamı için