Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 29

Animer les propriétés d'objet dans la boucle de rendu

Recommended resources

Bonjour !

Dans les modules précédents, nous avons construit de belles scènes 3D, mais elles restaient statiques, comme des photographies. Aujourd'hui, nous allons leur insuffler la vie. Nous entrons dans le module sur l'animation et l'interactivité, une étape clé pour créer des expériences web engageantes.

L'objectif de cette leçon est d'apprendre les bases de l'animation dans Three.js : nous allons animer les propriétés d'un objet, comme sa position et sa rotation, directement à l'intérieur de la boucle de rendu.

Vous vous souvenez de la fonction animate() ou render() que nous avons mise en place avec requestAnimationFrame ? C'est le moteur qui redessine notre scène environ 60 fois par seconde. Jusqu'à présent, elle ne faisait que rendre la scène telle quelle. Nous allons maintenant voir comment modifier nos objets à chaque "tic" de cette horloge pour créer du mouvement.

Le Cœur de l'Animation : La Boucle de Rendu

L'animation en 3D temps réel est une illusion, un peu comme un dessin animé. En modifiant très légèrement les propriétés d'un objet (sa position, sa rotation, sa taille) entre chaque image, puis en affichant ces images rapidement, notre cerveau perçoit un mouvement fluide.

La fonction qui s'exécute en boucle grâce à requestAnimationFrame est l'endroit idéal pour effectuer ces modifications.

Rotating Wireframe Cone Animation
Cette animation est le résultat simple de la modification de la propriété de rotation d'un objet à chaque image. C'est ce que nous allons apprendre à faire.

1. Animer la Rotation d'un Objet

Commençons par l'animation la plus simple et la plus courante : faire tourner un objet sur lui-même.

Pour cela, il suffit d'incrémenter l'une des propriétés de rotation de l'objet (rotation.x, rotation.y, ou rotation.z) à l'intérieur de notre boucle d'animation.

La vidéo suivante montre très clairement comment mettre cela en place.

Tuto THREE.JS [Javascript]

Cette section de la vidéo de Codeur de Nuit montre comment créer une fonction d'animation avec requestAnimationFrame et comment y ajouter une simple ligne de code pour faire tourner un cube.

Regardez de 00:05:36 à 00:06:39. Concentrez-vous sur la création de la fonction d'animation et la ligne qui incrémente la propriété rotation du cube.

Comme vous l'avez vu, le principe est simple. Si vous avez un objet cube dans votre scène, il suffit d'ajouter cette ligne dans votre fonction animate :

function animate() {
    requestAnimationFrame(animate);

    // Fait tourner le cube sur l'axe Y et l'axe X
    cube.rotation.y += 0.01;
    cube.rotation.x += 0.005;

    renderer.render(scene, camera);
}

La valeur 0.01 est en radians. Une rotation complète correspond à (environ 6.28). En ajoutant une petite valeur à chaque image, on obtient une rotation lente et continue.

Pour une explication textuelle qui détaille ce processus, vous pouvez consulter la documentation de MDN.

Réaliser une démo basique avec Three.js - MDN

Cet article de MDN Web Docs explique pas à pas comment ajouter l'animation dans une scène Three.js. Il couvre la mise en place de la boucle de rendu et l'animation de la rotation.

Lisez les sections "Animation" et "Rotation". Elles renforcent ce que vous venez de voir en vidéo avec des exemples de code clairs.


2. Animer la Position et l'Échelle

Le même principe s'applique à d'autres propriétés. On peut animer la position pour déplacer un objet, ou son échelle (scale) pour le faire grandir ou rétrécir.

Un simple cube.position.x += 0.01 ferait dériver le cube hors de l'écran. Pour créer des mouvements plus intéressants et contenus, comme une oscillation, on peut utiliser les fonctions mathématiques que vous connaissez bien en JavaScript, comme Math.sin().

La fonction sinus produit une vague qui oscille entre -1 et 1, ce qui est parfait pour créer un mouvement de va-et-vient.

function animate() {
    requestAnimationFrame(animate);

    // ... animation de la rotation ...
    
    // Fait osciller le cube de haut en bas.
    // On a besoin d'une variable qui évolue avec le temps.
    // Date.now() * 0.001 nous donne les secondes écoulées.
    const time = Date.now() * 0.001;
    cube.position.y = Math.sin(time); 

    renderer.render(scene, camera);
}

Dans cet exemple, cube.position.y va varier doucement entre -1 et 1, créant une lévitation fluide. La documentation MDN que vous avez consultée précédemment couvre également cette technique dans la section "Déplacement".


Le Problème du Framerate

La méthode objet.rotation.y += 0.01 a un défaut majeur : sa vitesse dépend de la fréquence de rafraîchissement de l'écran de l'utilisateur (le framerate).

  • Sur un écran standard à 60 Hz, le code s'exécute 60 fois par seconde.
  • Sur un écran de gaming à 144 Hz, il s'exécutera 144 fois par seconde, et l'animation sera donc plus de deux fois plus rapide !

Pour créer des applications professionnelles, les animations doivent avoir une vitesse constante, peu importe le matériel de l'utilisateur. La solution est de baser nos animations non pas sur le nombre d'images, mais sur le temps écoulé.

Three.js nous fournit un outil parfait pour cela : THREE.Clock.

Un objet Clock agit comme un chronomètre. On peut lui demander à tout moment le temps total écoulé depuis sa création.

// Créer l'horloge en dehors de la boucle d'animation
const clock = new THREE.Clock();

function animate() {
    // À chaque image, récupérer le temps écoulé depuis le début
    const elapsedTime = clock.getElapsedTime();

    // elapsedTime est maintenant une valeur en secondes qui augmente constamment.
    // On peut l'utiliser pour piloter nos animations.

    // La rotation est maintenant directement liée au temps.
    // La vitesse sera la même sur tous les écrans.
    cube.rotation.y = elapsedTime;

    // On peut aussi l'utiliser avec le sinus pour l'oscillation
    cube.position.y = Math.sin(elapsedTime);

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

Cette approche garantit une expérience cohérente pour tous les utilisateurs. La vidéo suivante explique très bien ce concept.

Particules 3D connectées avec Three.js

Dans cette section, Grafikart explique le problème de la dépendance au framerate et introduit THREE.Clock comme solution pour créer des animations basées sur le temps.

Regardez de 35:00 à 37:08. La première partie (jusqu'à 36:01) montre l'animation dépendante du framerate et explique le problème. La seconde partie introduit THREE.Clock et getElapsedTime() comme la solution robuste.


Atelier : Donnons vie à notre scène

Il est temps de mettre tout cela en pratique. Reprenez l'un de vos projets précédents, ou partez d'un code de base Three.js.

  1. Ajoutez un objet Mesh (un BoxGeometry avec un MeshBasicMaterial fera l'affaire).
  2. Créez une instance de THREE.Clock avant votre boucle d'animation.
  3. Dans votre fonction animate, récupérez le temps écoulé avec clock.getElapsedTime().
  4. Utilisez cette valeur elapsedTime pour animer à la fois la rotation et la position de votre objet.

Essayez ce code de départ :

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

// Scène, Caméra, Renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.z = 3;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// Contrôles
const controls = new OrbitControls(camera, renderer.domElement);

// Objet
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

// Horloge
const clock = new THREE.Clock();

// Boucle d'animation
function animate() {
    requestAnimationFrame(animate);

    // Récupérer le temps écoulé
    const elapsedTime = clock.getElapsedTime();

    // Animer la rotation
    cube.rotation.y = elapsedTime * 0.5; // Vitesse de rotation plus lente

    // Animer la position avec un sinus
    cube.position.x = Math.sin(elapsedTime);
    
    // Mettre à jour les contrôles
    controls.update();

    // Rendu de la scène
    renderer.render(scene, camera);
}

animate();

Expérimentez ! Changez les axes de rotation, multipliez elapsedTime par différentes valeurs pour changer la vitesse, ou utilisez Math.cos() pour la position sur un autre axe.


Conclusion

Félicitations ! Vous venez de faire vos premiers pas dans le monde de l'animation 3D. C'est une compétence fondamentale qui transformera vos projets statiques en expériences dynamiques et interactives.

Les points clés à retenir :

  • L'animation se produit dans la boucle de rendu (requestAnimationFrame) en modifiant les propriétés des objets à chaque image.
  • On peut animer les propriétés rotation, position, et scale pour créer du mouvement.
  • L'utilisation de += valeur pour l'animation est simple mais dépend du framerate, ce qui n'est pas idéal pour un résultat professionnel.
  • La solution robuste est d'utiliser THREE.Clock et sa méthode getElapsedTime() pour créer des animations basées sur le temps, garantissant une vitesse constante sur tous les appareils.

Aperçu de la prochaine leçon :
Nous avons vu comment utiliser le temps total écoulé (getElapsedTime) pour piloter nos animations. Dans la prochaine leçon, nous allons affiner cette technique en utilisant le "delta time" (clock.getDelta()), qui représente le temps écoulé depuis la dernière image. Cela nous donnera un contrôle encore plus précis sur la vitesse de nos animations, une technique indispensable pour la physique de jeu et les animations complexes.