4/5

4. Animation descriptive

Scène 3D possible après integration d’une animation

Interpolation par spline cardinale

Etat actuel de la scène

Considérez le code associé au fichier 3D_graphics/03_animation/interpolation_position
(mot clé SCENE_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\). La structure keyframes stocke l’ensemble \((p_i,t_i)_i\)

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}\).

Exemple du cas initial obtenu par interpolation linéaire. Notez la possibilité de déplacer les positions clés interactivement.


  • L’initialisation des positions et temps clés (variable keyframes) est réalisée dans la fonction setup_data alors que l’interpolation linéaire est calculée par la fonction linear_interpolation appelée elle même dans la fonction frame_draw.

    • Rem. C++ . Certaines signatures de fonctions sont qualifiées par le mot clé static (index_at_value, linear_interpolation). Ce mot clé (optionnel dans le cas présent) appliqué à une fonction simple permet d’indiquer qu’il s’agit d’une fonction à ne définir que localement dans ce fichier. De cette manière, d’autres fichiers .cpp pourrait également définir des fonctions possédant le même nom (avec une implémentation potentiellement différente) sans risquer de conflit.

  • Notez qu’il est possible de modifier interactivement les positions clés. Pour cela, appuyez sur shift pendant que vous sélectionnez puis déplacez une sphère d’une des positions de passage.

La gestion de la sélection et translation des sommets est réalisée dans les fonctions mouse_click et mouse_move. Vous pouvez utiliser ces deux fonctions telles quelles sans modifications dans la suite de l’exercice.

Votre objectif est de modifier la fonction de calcul du déplacement de la sphère bleue en passant de l’interpolation linéaire appliquée actuellement à une interpolation le long d’une spline cardinale (courbe lisse). Le déplacement résultant devant toujours passer par les points de controles aux instants correspondants.

Exemple de trajectoire obtenue lors de l’utilisation d’une spline cardinale.


On rappelle dans la suite le principe et les relations associées à l’interpolation par spline cardinale.

Spline cardinale

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\).

interpolation 01

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})\)

interpolation 02

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 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 = \mbox{constante} = 1\).


Synthèse

La Spline Cardinale interpolant les positions \(p_i\) aux instants \(t_i\) peut s’exprimer sous la forme suivante

\(\forall i\in[\![1,N-2]\!], \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}\)

  • \(\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

En suivant et adaptant le schéma utilisé pour la fonction d’interpolation linéaire (linear_interpolation), implémentez une fonction cardinal_spline_interpolation ayant la signature suivante:

vec3 cardinal_spline_interpolation(float t, float t0, float t1, float t2, float t3, const vec3& p0, const vec3& p1, const vec3& p2, const vec3& p3, float K)

Appelez cette fonction avec les paramètres adaptés depuis la fonction frame_draw.


Note:

  • Faites attention aux bornes extrémales des instants auxquels vous évaluez la spline afin de ne pas tenter d’accéder à des valeurs hors du buffer de positions clés. Pour cela, vous pourrez adapter les valeurs de timer.t_min et timer.t_max dans setup_data.

  • 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]\).



Animation hiérarchique

Cette seconde 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.
Chaque enfant hérite alors de la transformation du parent, et possède lui-même sa propre transformation. Il est généralement plus simple d’exprimer la position et les transformations à appliquer sur un élément dans le repère local de son parent.

Considérez le code associé au fichier 3D_graphics/03_animation/articulated_hierarchy (mot clé SCENE_ARTICULATED_HIERARCHY).
Ce code met en oeuvre un modèle articulé modélisé à l’aide de l’objet 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 personnage dans la fonction setup_data, et en particulier l’ajout d’un élément dans la hiérarchie hierarchy.add(…​).

    • Chaque élément est associé à un nom, qui permet de le retrouver dans la hiérarchie des éléments par la suite.

    • Le nom de l’élément parent doit être indiqué afin de connaitre sa position dans la hiérarchie. Rem. L’élément racine (root) a par convention un parent nommé "global_frame".

    • Chaque élément peut être associé à une transformation telle qu’une translation (par défaut nulle) et rotation (par défaut l’identité) par rapport à son parent.

La hiérarchie de la forme actuelle est illustrée par l’image suivante

hierarchy


Calcul des transformations dans le repère global

Notons \(L_{element}\) la matrice \(4\times 4\) représentant la transformation locale (comprend la rotation, la translation, scaling, etc.) d’un élement. Et \(G_{element}\), la matrice \(4\times 4\) représentant la transformation dans le repère global.

La relation hierarchique entre transformations est la suivante

  • \(G_{element} = G_{parent}\,L_{element}\)

  • avec \(G_{root}=L_{root}\) dans le cas particulier de la racine.

Dans le cas de l’objet actuel, on aura par exemple

  • \(G_{arm} = L_{body}\,L_{shoulder}\,L_{elbow}\,L_{arm}\)


Les translations et rotations associées aux éléments de la hiérarchie sont stockées explicitement dans la structure hierarchy_mesh_drawable_node afin de permettre leur modification au cours d’une animation.

Observez la fonction d’affichage frame_draw.

  • Les translations et rotations des différents éléments peuvent être accédées et modifiées pour réaliser une animation.

  • Notez qu’il s’agit des translations et rotations exprimées localement par rapport au repère de l’élément parent à celui désigné.

  • L’appel à hierarchy.update_local_to_global_coordinates permet de mettre à jour les transformations de chaque élément dans le repère global. Celles-ci sont utilisées en tant que paramètres uniformes pour chaque élément lors de l’affichage.


Notez que le code utilise une structure d’aide hierarchy_mesh_drawable_display_skeleton qui permet d’afficher le squelette et les repères de la hierarchie visuellement par un simple appel à draw. Cette structure permet de vous aider en cas de debug lors de la mise en place d’une hierarchie et des transformations.

Exercice

Modifiez la forme et l’animation de l’objet pour modéliser 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)


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).

  • La syntaxe hierarchy.add({mesh_drawable(), "parent_name"}); permet d’ajouter un noeud à la hierarchie sans représentation visuelle - 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 hierarchie.

Scène finale

  • Incorporez 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.