Séance 5: Animation
Scène 3D possible après integration d'une animation
Animation hiérarchique
Code associé à cette partie: scenes_inf443/05_animation/a_hierarchy Cette partie s'intéresse à la modélisation d'une forme articulée.Les formes articulées sont généralement modélisées par des primitives reliées entre elles par des relations hiérarchiques:
- ex. pour un personnage dont la racine se situe généralement au niveau du bassin: les bras, jambes et la tête sont des enfants du corps; les pieds étant eux même des enfants de la jambe, etc.
Explication de la structure hiérarchique
Ce code met en oeuvre un modèle articulé modélisé à l'aide d'une structure hierarchy_mesh_drawable.La structure hierarchy_mesh_drawable modélise une hiérarchie d'éléments. Chaque élément est associé à une transformation (ex. translation, rotation) exprimée localement par rapport au repère de son parent. Avant l'affichage, les transformations exprimées dans le repère global sont mises à jour en suivant la hiérarchie des éléments.
Compilez et exécutez le code et vérifiez que vous obtenez une animation similaire à la video suivante.
Observez la création du modèle hiérarchique dans la fonction initialize(). Chaque élément/noeud de la hierarchie est associé à une structure hierarchy_mesh_drawable_node. Ce noeud contient:
- - Un mesh_drawable ([node].drawable) le contenu affichable correspondant ainsi que le nom de l'élément actuel (défini dans le mesh_drawble).
- - Le nom de l'élément ([node].name)
- - Le nom de l'élément parent ([node].name_parent)
- - La transformation relative par rapport au parent ([node].transform_local)
- - Le premier argument est le mesh_drawable.
- - Le second argument est la nom associé à cet élément.
- - Le troisième argument est le nom de l'élément parent, afin de connaitre la position du noeud dans la structure hierarchique.
-
- - Le tout premier élément ajouté dans la hiérarchie est la racine/root. Le nom de son parent n'a pas à être indiqué explicitement à ce moment (il est choisi par défaut pour être nommé global_frame). Il ne doit y avoir qu'une seule racine par hiérarchie.
- - Les éléments suivants doivent par contre indiquer explicitement le nom d'un parent qui a déjà été ajouté dans la hiérarchie.
- - Le quatrième argument est facultatif. Il s'agit d'une translation ou d'une transformation affine (affine_rts) correspondant à la transformation relative vis à vis du repère parent.
-
- La transformation est par défaut l'identitée, et il est possible de modifier cette transformation plus tard.
- - Les transformation locales (relatives au repère du parent) peuvent être modifiées avec la syntaxe hierarchy[nomElement].transform_local.translation=... ou hierarchy[nomElement].transform_local.rotation=....
-
- Si vous aviez indiqué une transformation à l'initialization, cela revient à ré-écrire dessus.
- - Attention important: Il est ensuite nécessaire d'appeler la fonction hierarchy.update_local_to_global_coordinates(). Cette fonction vient mettre à jour les transformations de chaque noeud afin de les exprimer dans le repère absolue en parcourant la hiérarchie. Si vous oubliez cet appel, les transformations hiérarchiques ne seront pas appliqués.
Exercice: Ajout d'éléments
> Adaptez le code de manière à modéliser l'animation suivante:- - Deux cylindres jaunes sont ajoutés aux extrémités et soumis à un mouvement de rotation.
- - Un mouvement d'oscillation est ajouté à la racine de la hiérarchie.
- Notez que grâce à l'utilisation de la hierarchie, l'ajout d'une transformation sur la racine se propage automatiquement à l'ensemble des éléments enfants.
- - Essayez d'ajouter et d'appliquer la rotation sur les cylindres jaunes lorsque la racine n'oscille pas - cela facilite le debug.
Ainsi visuellement:
- - Si votre objet possède des coordonnées centrées autour de l'origine, alors la rotation le fera tourner autour de son centre.
- - Si votre objet possède des coordonnées non-centrées, il tournera autour du point corespondant aux coordonnées (0,0,0).
- \(\Rightarrow\) En fonction du point autour duquel vous souhaitez faire tourner votre objet, il peut être intéressant de créer votre forme initiale de manière centrée ou non afin d'éviter des calculs plus complexes de translation ultérieurs.
- Rem. Dans le cas d'une hiérarchie, l'"origine" est l'origine du repère parent.

Application d'une rotation sur un cylindre dont les coordonnées sont: centrées à gauche, sur l'un des bord à droite.
Exercice: Oiseau
Tentez désormais de créer la forme et l'animation d'une forme animée ressemblant à un oiseau. Exemple de modèle possible(vous êtes libre de générer un modèle ayant une autre apparence et de l'adapter à votre convenance) Aide:
- - Le corps allongé de l'oiseau peut être généré par le biais de la fonction mesh_primitive_ellipsoid lors de la création du maillage, ou en déformant une sphère (structure mesh_drawable) avec [mesh_drawable].model.scaling_xyz=vec3{sx,sy,sz}.
- - Les ailes peuvent être générés comme des quadrangles (mesh_primitive_quadrangle). Pensez à choisir des coordonnées telles que la rotation des ailes se fasse atour d'une des arêtes (et pas de manière centrée au milieu du quadrangle).
Remarques si vous souhaitez utiliser la structure hiérarchique pour des modèles plus avancés.
-
- En plus des transformations gérées par la hiérarchie, il est possible de définir directement des transformations sur les paramètres uniformes des structures mesh_drawable (comme cela était fait classiquement). Ces transformations seront combinées à celle définie par la hiérarchie, mais appliquée uniquement à l'élément (et non pas à ces descendants).
- - Il est possible d'ajouter un noeud à la hierarchie sans représentation visuelle (ex. hierarchy.add(mesh_drawable(), "currentName", "parentName");) - mais possédant une transformation applicable à tous ces descendants
-
- ex. objets tournant autour d'un centre particulier sans que celui-ci ne soit lui même une surface visible.
- - Il est possible d'affecter des shaders et textures différentes aux éléments visuels (mesh_drawable) composant la hiérarchie
(Supplément non obligatoire) Interpolation par spline cardinale
Etat actuel de la scène
Considérez le code présent dans le répertoire scenes/inf443/05_animation/b_interpolation_position Ce code modélise une série de positions clés \(p_i\) affichées par des sphères blanches. Chaque position est associée à un "instant clé" \(t_i\). Les variables key_positions et key_times définies dans la fonction d'initialisation définissent respectivement les suites de positions et temps clés par lequels nous souhaitons réaliser l'interpolation. Le déplacement de la sphère bleue au cours du temps correspond pour l'instant à l'interpolation linéaire entre ces positions clés. Celle-ci étant donnée par la relation \(\forall t\in[t_i, t_{i+1}]\;,\;\;p(t) = (1-\alpha)\, p_i + \alpha\, p_{i+1} \), avec \(\alpha=\frac{t-t_i}{t_{i+1}-t_i}\). L'interpolation étant appelée à chaque frame (dans la fonction d'affichage) par la lignevec3 p = interpolation(t, keyframe.key_positions, keyframe.key_times);
Exemple du cas initial obtenu par interpolation linéaire. Notez la possibilité de déplacer les positions clés interactivement.
Exemple de trajectoire obtenue lors de l'utilisation d'une spline cardinale.
Spline cardinale
On rappelle si nécessaire dans la suite le principe et les relations associées à l'interpolation par spline cardinale (rappel du cours).Expression en cubique de Hermite
Considérons deux positions \(p_0\) et \(p_{1}\), associées à deux vecteurs dérivées \(d_0\) et \(d_{1}\). Supposons dans un premier temps que \(p_0\) est associé à l'instant \(0\), et \(p_1\) est associé à l'instant \(1\).
La courbe \(p(t)\) interpolant ces positions et dérivées entre \(t=0\) et \(t=1\) doit satisfaire les contraintes suivantes
\(\left\{ \begin{array}{l} p(0) = p_0 \\ p(1) = p_1 \\ p ' (0) = d_0 \\ p ' (1) = d_1 \end{array} \right.\)En supposant que la fonction \(p:t\to p(t)\) soit exprimée sous la forme d'un polynome cubique \(p(t)=a t^3 + b t^2 + c t + d\), l'ensemble des contraintes se traduit par un système linéaire à 4 inconnus
\(\left\{ \begin{array}{r} d = p_0 \\ a+b+c+d = p_1 \\ c = d_0 \\ 3 a + 2 b + c = d_1 \end{array} \right.\)La résolution de ce système abouti au polynome solution
\(p(t)=(2 p_0 - 2 p_1 + d_0 + d_1) t ^ 3 + (-3 p_0 + 3 p_1 - 2 d_0 - d_1) t ^ 2 + d_0 t + p_0 \)Ou encore, reformulé suivant la représentation dite de Hermite, en regroupant les termes suivants \((p_0,p_1,d_0,d_1)\)
\(p(t) = (2 t^3 - 3 t^2 + 1)\, p_0 + (t ^ 3 - 2 t ^ 2 + t)\, d_0 + (-2 t ^ 3 + 3 t ^ 2)\, p_1 + (t ^ 3 - t^2)\, d_1 \)Prise en compte de la plage de valeur du paramètre temporelle
D'une manière plus générale lorsque l'on cherche à interpoler un ensemble de positions clés, les sommets \((p_0, p_1)\) vont correspondre aux positions \((p_i,p_{i+1})\) qui sont elles même associés à des instants \((t_i,t_{i+1})\) quelconques. On supposera similairement que les dérivées sont données par \((d_{i}, d_{i+1})\)
Afin de prendre en compte la plage de variation du paramètre \(t\), on applique un changement de variable transformant l'intervalle \([t_i,t_{i+1}]\) vers \([0,1]\).
Le polynome de Hermite interpolant des deux positions et dérivées peut alors s'exprimer sous la forme
- \(\forall t \in [t_i,t_{i+1}]\;,\;\; p(t) = (2 s ^ 3 - 3 s ^ 2 + 1)\, p_i + (s ^ 3 - 2 s ^ 2 + s)\, d_i + (-2 s ^ 3 + 3 s ^ 2)\, p_{i+1} + (s ^ 3 - s^2)\, d_{i+1}\)
- Avec \(\displaystyle s=\frac{t-t_i}{t_{i+1}-t_i}\).
Expression en position: Spline Cardinale
La dernière étape consiste à exprimer le polynome en utilisant uniquement des données de positions à la place des deux dérivées. La "spline cardinale" consiste à exprimer ces dérivées sous la forme de différences finies par rapport aux positions voisines.
On pourra considérer- \(\displaystyle d_i = 2\,K\;\,(p_{i+1}-p_{i-1}) / (t_{i+1}-t_{i-1})\)
- \(\displaystyle d_{i+1} = 2\,K\;\,(p_{i+2}-p_{i}) / (t_{i+2}-t_{i})\)
Avec \(K\in[0,1]\) un paramètre relié à la notion de "tension" de la spline.
- \(K=0\): Courbe la plus tendue: chaque morceau est un segment de droite
- \(K=0.5\): Tension dite naturelle, également appelée spline de Catmull-Rom
- \(K>0.5\): Tangente de plus en plus marquée aux points de controles. La trajectoire s'éloigne du polygone de controle, et des points d'inflexions de la courbe apparaissent entre les points de controles.
Notez qu'il s'agit d'une version un peu plus générale que celle vue en cours en prenant en compte des intervalles de temps quelconques. Vous retrouverez la formulation du cours en supposant \(\forall i\;,\;t_{i+1}-t_i = constante = 1\).
Synthèse
La Spline Cardinale interpolant les positions \(p_i\) aux instants \(t_i\) peut s'exprimer sous la forme suivante
-
\(p(t) = (2 s ^ 3 - 3 s ^ 2 + 1)\, p_i + (s ^ 3 - 2 s ^ 2 + s)\, d_i + (-2 s ^ 3 + 3 s ^ 2)\, p_{i+1} + (s ^ 3 - s^2)\, d_{i+1}\)
- \(\displaystyle s=\frac{t-t_i}{t_{i+1}-t_i}\)
- \(\displaystyle d_i=2 K \, \left(\frac{p_{i+1}-p_{i-1}}{t_{i+1}-t_{i-1}}\right)\)
- \(\displaystyle d_{i+1}=2 K\,\left(\frac{p_{i+2}-p_{i}}{t_{i+2}-t_{i}}\right)\)
Exercice: Interpolation
En suivant et adaptant le schéma utilisé pour l'interpolation linéaire, implémentez l'interpolation par spline cardinale. Pour cela- - Complétez la fonction cardinal_spline_interpolation dans le fichier interpolation.cpp. On identifiera \(t_0=t_{i-1}\), \(t_1=t_{i}\), \(t_2=t_{i+1}\), \(t_3=t_{i+2}\), avec \(t\in[t_{i},t_{i+1}[\).
- - Adaptez la fonction interpolation pour appeler cardinal_spline_interpolation avec les bons paramètres.
- On pourra considérer dans un premier temps \(K=0.5\) (cas particulier appelé Spline de Catmull-Rom, ou Spline naturelle). Observez les changements de trajectoires lorsque \(K\) varie sur l'intervalle \([0,1]\).
Scène finale
Si jamais vous êtes en avance, vous pouvez tenter d'incorporer votre élément animé dans votre scène de terrain.-
Faite en sorte que le mouvement de l'oiseau (translation de l'élément racine) suive une trajectoire définie par positions clés et calculée par spline cardinale.
-
(extension) Réfléchissez à une manière d'adapter l'orientation de votre personnage animé lors de son mouvement.
- (extension) Il est possible de faire en sorte que l'élément animé se déplace à vitesse (approximativement) constante: pour cela, adaptez les instants associés aux points de contrôle en fonction de la longueur des segments du polygone de controle.
Rem. Les extensions, ainsi que l'incorporation de l'interface de manipulation des points de contrôle dans votre scène demande un travail conséquent. Ne passez pas trop de temps dessus si vous n'êtes pas en avance.