Projet image 2015 : Transfert d'animation : Animer des personnages virtuels par le jeu et le mime

De Ensiwiki
Aller à : navigation, rechercher
Project schedule.png
Titre du projet Transfert d'animation : Animer des personnages virtuels par le jeu et le mime
Cadre Projets de spécialité
Page principale Projets de spécialité image

Encadrants Marie-Paule Cani, Antoine Begault

Étudiants

Cadrage du projet

Contexte des travaux et problématique

Notre projet consiste en l'animation de personnage dans une scène 3D. Il s'inscrit dans le projet Figurine, qui est le fruit de la collaboration des équipes Imagine et Prima de l'INRIA, dont l'objectif est de modéliser numériquement et de façon intuitive une scène jouée avec des figurines. Des enfants pourront alors jouer avec des figurines équipées de différents capteurs, et pourront ensuite voir leurs figurines prendre vie et refaire la scène jouée, dans un monde 3D.

Notre travail est de faire l'animation des personnages virtuels à partir des mouvements que l'animateur a fait faire à la figurine. De plus, si le manipulateur filmé a l'air triste, content ou en colère, nous devons transcrire ces émotions sur le personnage virtuel représentant la figurine. Nous utiliserons au cours de ce projet le logiciel expressive, actuellement en cours de développement à l'INRIA, qui permettra de modéliser des scènes 3D de manière intuitive.

Figurine de la princesse

Objectifs du projet

  • Nos objectifs sont multiples. Nous développerons des techniques d'animations diverses que nous intégrerons au logiciel expressive. Nous créerons les différentes animations que les personnages seront à même de faire, comme la marche, le saut ou la course. Nous mettrons en suite en place le transfert des émotions du manipulateur de la figurine au personnage virtuel (on peut imaginer qu'un personnage triste qui marche aura les bras ballants et la tête baissée, tandis que le même personnage joyeux aura un déplacement plus léger, voire sautillant). Nous avons jusqu'au 11 juin pour réaliser ce travail.
  • La délimitation de nos objectifs est claire : nous mettrons de côté la diversité des figurines, et nous nous concentrerons sur les figurines humanoïdes. D'autre part, nous n'aurons pas à modéliser ces figurines, nous les animeront seulement : les modèles 3D de chaque figurine sont fournis.. Détecter si le manipulateur de la figurine est en colère, triste ou heureux ne sera pas non plus de notre ressort : nous transférerons cette émotion que l'on nous donnera au personnage virtuel.
  • Plusieurs indicateurs nous permettrons de quantifier l'avancée du projet. Nous prendrons pour indicateur sur l'avancement des livrables le nombre d'animations que nous aurons mises en place (courir, sauter…). Pour l'indicateur sur la qualité de notre livrable, nous considérerons le degré de réalisme de nos animations.

Planning

Notre projet se déroulera en plusieurs phases.

  • Tout d'abord, il y aura une phase préliminaire qui consistera à assimiler la documentation conseillée par notre tuteur. En effet, participant à un projet orienté recherche, nous devrons nous atteler à la lecture de nombreux articles scientifiques afin de s'approprier le contexte du projet, de savoir ce qui se fait dans ce domaine là, quelles sont les techniques que nous devrons implémenter et celles qui ne sont pas directement applicables à notre projet mais que nous devrons adapter. Dans cette première phase, nous ferons aussi la conception de la structure applicative, afin que notre travail s'intègre pleinement au logiciel expressive. Ce travail est indispensable et doit être effectué en amont. Nous assimilerons aussi en partie l'architecture d'expressive, afin de pouvoir l'utiliser au mieux et être efficace par la suite.
  • Une fois cette phase terminée, nous adopterons une approche incrémentale du plus simple au plus complexe afin d'avoir des résultats concrets tout au long de notre avancée dans le projet. Nous mettrons en place l'animation rigide, puis les animations successives de la marche, de la course puis du saut. À chaque phase où nous rajouterons une fonctionnalité, nous procéderons de la manière suivante : nous mettrons en place l'animation en dehors de l'application, avec une technologie différente. Puis une fois un résultat satisfaisant obtenu, nous le présenterons à notre tuteur qui le validera ou nous demandera de peaufiner. Après validation de notre tuteur, nous incorporerons la nouvelle fonctionnalité à Expressive.

Organisation

Étant une équipe composée de 4 étudiants, nous avons répartis les rôles afin de paralléliser les tâches qui étaient réalisables en même temps et ainsi de profiter au mieux de notre nombre. Nous avons utilisé un trello pour clarifier cette répartition. Palisse Benjamin sera chargé d'analyser les données fournies par les capteurs dont sont équipées les figurines. Maxime Garcia et Lebbe Nicolas se chargeront de mettre aux points les différentes techniques d'animation, et les animations en elles mêmes dans un langage intermédiaire. Touzeau Valentin devra alors incorporer ses animations au sein du projet expressive une fois que notre tuteur aura validé le travail de Maxime et de Nicolas.

D'autre part, deux d'entre nous ont pas suivi le cours de graphique 3D. Nous travaillerons donc en binôme sur les tâches nécessitant des connaissances en graphique 3D : un d'entre nous aura suivi le cours, et l'autre non. Nous organiserons ainsi une monté en compétences entre nous. De même, l'un d'entre nous est en filière ISI (les 3 autres sont en MMIS) et se verra alors attribué la partie conception présente dans le projet.

La manière dont nous communiquerons tout au long du projet sera très simple. Nous travaillerons tous les 4 dans la même salle à l'Ensimag. Cela permettra d'avoir une communication vraiment aisée entre nous, et que l'un d'entre nous ne reste pas bloqué trop longtemps sur le même problème. Nous organiserons une réunion chaque matin, afin de vérifier l'avancement du projet et de mettre en avant les tâches que nous aurons à faire durant la journée. Quand à la communication avec notre tuteur, nous avons deux rendez-vous par semaine, à l'Ensimag ou directement dans les locaux de l'INRIA. Ainsi, nous ferons régulièrement le point avec lui, afin qu'il valide certaines étapes du projet et nous donne des pistes à exploiter pour la suite. Dans l'un de ces deux rendez-vous hebdomadaire, Marie-Paule Cani, chef de l'équipe Imagine et enseignante du cours de graphique 3D à l'Ensimag, sera présente afin d'apporter un autre angle de vue.

Travail réalisé

Reconnaissance d'état

Comme nous ne pouvions pas utiliser les données recueillies par le capteur lors de notre propre expérience dans les locaux de l'INRIA, nous avons mis au point un éditeur de trajectoire qui nous permettrait d'avoir des données à traiter. Cet éditeur est disponible ici


Un exemple d'utilisation est disponible ci-dessous.

Courbe en train d'être éditée, on place les points de contrôle dans le plan (x,y) puis on choisi leur coordonnée en z


À partir des points obtenus par l'éditeur de courbe, nous créons une courbe en 3D, et nous attribuons à chaque portion de cette courbe un état immobile, marche, course, saut, fin saut, vol, attaque. Pour faire cette attribution, nous nous basons sur les caractéristiques de la courbe. L'étude de ces caractéristiques nécessite que la courbe soit lissée au préalable car nous nous basons sur des calculs de moyenne de dérivées pour déterminer l'action courante ; avoir trop de bruit fausserait donc ces valeurs. La détection d'un saut ou d'une attaque se base sur l'étude des dérivées première et seconde de la courbe et des orientations selon la composante verticale. Pour les autres actions on regarde seulement dans quel intervalle (en fonction de moyennes calculées à partir de la trajectoire lisse) de valeurs, les dérivées première sont comprises (pour le vol on regarde aussi l'altitude).

Par exemple pour détecter un saut on cherche les points d'inflexions de la courbe (ce qui correspond à une annulation de la dérivée seconde avec changement de signe) et les moments où la dérivée seconde en altitude prend une grande valeur (c'est à dire une variation brusque de la dérivée en altitude). Lors de la détection on distingue deux type d'états : les états de déplacement ( immobile, marche, course,vol) et les actions (saut, fin saut, attaque). On peut alors éviter des détections d'actions qui n'en sont pas (qui sont en fait soit du bruit ou soit une imprécision du capteur) en imposant qu'une action ait une durée minimale et en passant d'un état de déplacement à un état d'action si celle-ci dure suffisamment longtemps.

Voici un schéma illustrant la machine à états que représente le détecteur :

State reconnaissance.png
Exemple d'un parcours de la courbe en faisant afficher, par coloration du cube, les différentes actions détectées

Marche/Course

La marche a tout d'abord été réalisé en Javascript (avec des canvas HTML5) avec des stickman. Ce travail est visible suivant ce lien

Détermination des courbes du mouvement

Afin de pouvoir créer des animations procédurales qui fonctionnent quel que soit la vitesse V et le "degré de joyeuseté" H de notre humanoïde, nous avons décidé d'opter pour définir le mouvement du personnage à l'aide de courbes paramétrés cycliques.

Arrêt/Marche/Course

Pour déterminer ces courbes nous sommes partis de mouvements cycliques de base à partir de cosinus et sinus que nous avons ensuite complexifié jusqu'à trouver des courbes dépendantes à la fois de V et de H.

Différentes vitesses de marche
Stickman marchant doucement
Stickman marchant normalement
Stickman qui court
Emotions

Afin d'ajouter davantage d'expressivité à nos animations, nous avons pris en compte si le personnage était heureux ou non dans sa manière de se déplacer.

En pratique, plus le personnage est triste plus il aura les bras ballants, le corps et la tête penché vers l'avant. Et inversement, plus le personnage est heureux plus il aura tendance à avoir des mouvements amples et le torse bombé.

Différents degrés de joyeuseté à même vitesse
Stickman triste
Stickman heureux


Intégration à Expressive

Ces animations sont ensuite appliquées sur le modèle 3D suivant:

Bonhomme en 3D, créé avec le logiciel expressif

La slide bar en bas permet de revenir au moment souhaité lors de l'animation.

Il est aussi possible de faire courir le personnage: cela a pour effet d'augmenter l'amplitude des mouvements de ses bras et de ses jambes lors de son déplacement.

Saut

L'animation du saut a elle aussi d'abord été réalisé en Javascript avec des stickmans. Ce travail est visible suivant ce lien (dessiner une parabole !)

Les étapes du saut
Jump anim.png

Implémentation

Le saut est décomposable en 5 étapes :

  1. Phase d'anticipation (fléchissement des genoux/descente du bassin) → anticipation
  2. Phase de saut avec pieds au sol → ground-jump
  3. Phase de saut avec pieds en l'air → mid-air
  4. Phase de de descente → fall
  5. Phase de récupération → recovery

Emotions

L'utilisateur a la possibilité de tracer la trajectoire 2D du saut, le stickman sautera alors en suivant cette trajectoire.
De même que précédemment, l'animation du saut est adapté à l'humeur du personnage, en fonction de si ce dernier est triste ou heureux il lèvera plus ou moins les bras lors de son saut.

Attaque

En se qui concerne l'attaque nous nous somme basé sur une technique d'animation par ligne d'action dynamique([3]) en utilisant une simplification de cette dernière. Nous dessinons (ou calculons) une trajectoire qu'un ou plusieurs membres du squelette va suivre tout en donnant une orientation au os (s'il y en a) par rapport à cette courbe (on peut par exemple parcourir la courbe de façon tangentielle ou de façon orthogonale). A chaque déplacement sur la courbe on vérifie que les os gardent la même longueur et on rectifie si ce n'est pas le cas par une méthode displacement constraint (cf Vêtements/élasticité). Lors de l'animation il est possible de préciser que certaines articulations du squelette restent fixes, la rectification des longueurs des os prendra cette contrainte en paramètre.

Deux émotions sont applicables à cette animation : la tristesse et la peur. Dans le cas de la tristesse nous jouons sur l'amplitude des courbes que les membres du personnage vont suivre (moins ample s'il est triste) alors que dans celui de la peur nous perturbons ces courbes par du bruit de sorte que le personnage se mette à trembler.

Voici une illustration de l'animation avec deux paramètres de peur différents :

Deux animations de la même attaque : à gauche un paramètre faible de peur, à droite un paramètre élevé
AttaquePeur.png AttaquePeur2.png

Cette méthode d'animation à l'avantage d'être intuitive (puisqu'il s'agit de donner des courbes de mouvement) et de pouvoir incorporer des émotions, mais peut devenir fastidieuse à utiliser si l'on veut un certain degrés de détail sur l'animation (il faut dessiner un plus grand nombre de courbes et bien synchroniser les déplacements sur ces dernières).

Mort

L'animation de la mort du personnage est faite de manière physique. Chaque articulation du personnage est considérée comme étant une sphère de masse m (la même pour chacune des articulations) soumise à la gravité. A chaque pas de temps on modifie la vitesse et la position de chaque articulation (en résolvant le PFD) puis on rectifie ces dernières par la même méthode (décrite dans Vêtements/élasticité) de conservation des distances des os que pour l'attaque tout en prenant en compte les collisions avec le sol et entre sphères.

Voici deux illustrations de l'animation de mort :

Mort.png Mort2.png

Dans le cas présent l'implémentation de cette méthode physique était plutôt simple (peu de paramètres à prendre en considération) mais les résultats peuvent être moins réalistes que ce que l'on pourrait attendre d'une animation physique. Ceci est dû notamment à la simplicité du squelette que nous considérons et au fait que nous ne rectifions pas les angles entre deux os adjacents mais seulement leur longueur.

Vêtements

Nous avons implémenté une simulation physique de vêtements en Javascript avec la technologie Webgl.

Deux étapes de la chute d'une jupe sur des sphère
Jupe 1.png Jupe 2.png

Pour implémenter les vêtements, nous avons simplement considérer que ceux-ci étaient de la forme suivante :

+---+---+---+ avec + les particules
|   |   |   |      - et | les liens de longueurs fixes
+---+---+---+
|   |   |   |
+---+---+---+
|   |   |   |
+---+---+---+ 

Mouvement des particules

Intégration de Verlet : (formule de Taylor en t+dt et t-dt)

\overrightarrow{x}(t+dt) = 2\overrightarrow{x}(t) - \overrightarrow{x}(t-dt) + dt^2 \overrightarrow{a}(t) + o(dt^3)


Principe fondamental de la dynamique :

\sum \overrightarrow{F}(t) = m \overrightarrow{a}(t)

D'où le pseudo-code suivant pour la simulation du mouvement :

Vector g(0.0, 0.0, -9.81)
structure Point {
    Vector position, last_position;
    Float mass;
}
function move(Point p, Float dt) {
    Vector a = g / p.mass; // PFD
    Vector tmp = p.position;
    p.position = 2.0*tmp - p.last_position + dt*dt * a; // verlet integration
    p.last_position = tmp;
}

Élasticité

Nous avons décidé d'utilisé une méthode issu des jeux-vidéos ([1]) qui a l'intérêt d'être très peu coûteuse en temps de calcul et d'être stable (ne fera jamais diverger les points) quelque soit les déformations effectuées sur le vêtement.

Celle-ci consiste à résoudre les distances imposés par les liens en forçant les points à revenir à la bonne distance. Autrement dit, après avoir bougé tous les points, on calcul les distances entre tous les points qui sont reliés par une distance fixe et on les translates proportionnellement à leur masse afin qu'il soient bien à la bonne distance l'un de l'autre.

structure Link {
    Point p0, p1;
    Float length;
}
function solveLink(Link l) {
    Vector v = l.p1 - l.p0;
    Float distance = v.norm();
    Float massTotal = l.p0.mass + l.p1.mass;
    l.p0 -= v * (distance - l.length) * l.p0.mass/massTotal;
    l.p1 += v * (distance - l.length) * l.p1.mass/massTotal;
}

En itérant plusieurs fois ce processus on peut espérer obtenir des points à peu près aux bonnes distances les uns des autres, le nombre d'itérations effectuées permet de choisir l'élasticité du vêtement. (plus il y a d'itération plus le vêtement sera rigide)

Cette méthode peut se justifier physiquement en montrant qu'elle est l'analogue d'un système de ressorts avec des constantes de raideurs infinis. Une justification mathématique peut aussi être produite (voir [1]) montrant que les itérations convergent vers une configuration du vêtement vérifiant les longueurs désirées.

Résolution des collisions

Après la collision

Détection "après" la collision, c'est-à-dire qu'à chaque itération on vérifie que la particule ne se trouve pas dans une sphère et, le cas échéant on cherche la plus petite distance possible pour qu'elle ne soit plus en collision avec la sphère ou elle se trouve.

structure Sphere {
    Point center;
    Float radius;
}
function solveCollision(Point* p, Sphere S) {
    Vector v = *p - S.center;
    if(v.norm() < S.radius) {
        *p += v * (S.radius - v.norm());
    }
}

+ Simple à adapter à tout type d'objet (surfaces implicites).
- Ne fonctionne pas si les points se déplacent à grande vitesse.

Avant la collision

Détection "avant" la collision en prévoyant où la collision aura lieu par rapport à la vitesse de la particule.

function solveCollision(Point* p, Sphere S, Vector velocity) {
    Vector v1 = S.center - *p;
    Float ps = dotProduct(S.center, v1); // on projette le centre de la sphère sur le vecteur vitesse
    // le point projeté correspond au point dans le temps ou la particule est la plus proche de la sphère
    if(ps >= 0.0 && ps < velocity.norm()) { // si il y a effectivement collision durant ce déplacement
        Vector v2 = ps * velocity/velocity.norm();
        Float d = sqrt(S.radius*S.radius - v2.norm()*v2.norm());
        *p -= velocity/velocity.norm() * d;
    }
}

+ Plus réaliste.
- Collision avec des sphères uniquement.

Rebonds

Les rebonds peuvent être simplement mis en place à l'aide du point de contact entre le point et la sphère (obtenu par l'une des deux méthodes précédentes) et la normale en ce point.

Dans le cas de vêtements, ceux-ci ne rebondissant pas, nous n'avons pas eu à l'implémenter.

Implémentation

Bien que nous n'ayons pas eu le temps nécessaire pour intégrer toutes nos animations au logiciel expressive fournit par l'INRIA, nous avons mis en place la structure logicielle permettant cette intégration. Nous avons commencé par écrire des classes Skeleton et Articulation réalisant l'interface entre nos animations et le squelette de rendu fourni par expressive (WeightedGraph et WeightedPoint). Nous avons également créé une classe Posture, qui permet de sauvegarder et de restaurer une position donnée du squelette d'animation. Ces postures sont ensuite manipulées par les classes de mouvements (AbstractMotion, WalkingMotion et JumpingMotion). La posture du squelette est calculée à partir d'une posture initiale et du temps t écoulé depuis cette posture. Pour faciliter la création de nouveaux mouvements, la classe AbstractMotion implémente des classes réutilisables permettant de manipuler les membres du squelette. UmlExpressive.png

Conclusion

Problèmes rencontrés

Au fur et à mesure que le projet se déroule, nous avons cependant repéré des risques et des problèmes qu'il est possible que nous rencontrions, certains auxquelles nous nous attendions et d'autres qui nous ont surpris. Les risques sont variés.

Tout d'abord, ce projet de recherche étant extrêmement libre, nous avons carte blanche sur la manière dont nous pouvons faire les choses, les méthodes que nous pouvons employer. Nous faisons alors un gros travail de documentation, et nous implémentons des méthodes proposées dans les articles scientifiques proposés par notre tuteur, mais ce temps n'aboutit pas toujours à un résultat convaincant (résultat pas assez générique par exemple), ce qui provoque donc des pertes de temps. La fréquence élevée des entrevues avec notre tuteur permet d'atténuer en partie ce problème. D'autre part, nous travaillons tous les 4 sur nos machines personnelles, qui sont souvent âgées et loin d'être aussi performantes que celles dont l'INRIA dispose. Nous avons donc mis beaucoup de temps en début de projet à mettre en place l'environnement sur nos machines personnelles (problèmes de compatibilité) et compiler le projet sur nos machines est encore long, même lorsque les modifications apportées sont minimes. Cependant, après que nous ayons fait part de ce problème à notre tuteur, ce dernier nous a proposé d'utiliser le matériel de l'INRIA de manière ponctuelle. Nous aurons alors la possibilité dans certains cas de faire exécuter notre code sur leurs machines.

Le fait d'avoir des compétences variées nous permet de paralléliser les tâches de manière efficace, mais cela entraîne que certaines compétences critiques ne sont détenues que par 1 ou 2 membres du groupe. Ce défaut se gommera au fur à mesure de l'avancée du projet grâce à la montée en compétences que nous organisons.

Le projet expressive est vieux de plus d'un an, et il est difficile de l'assimiler étant donné le temps que nous possédons. Ainsi, nous ne connaissons que quelques parties du code, et nous risquons de réimplémenter des choses déjà existantes dans le projet, ce qui est une perte de temps. Nous avions pressenti ce risque, d'où l'intérêt d'avoir consacré du temps au cours de la première phase du projet pour assimiler l'architecture d'expressive. Les entrevues régulières avec notre tuteur nous permettent également de poser des questions sur certaines parties nous paraissant obscure, mais il arrive parfois que nous faisons du travail superflu.

Le début du projet nécessitait d'avoir les données collectées par les capteurs des figurines après qu'un animateur les ait faites bouger. Cette dépendance nous a bloqué au début du projet. Comme les données directement obtenu des capteurs étaient inexploitables (à cause du bruit), nous avons donc crée nous mêmes nos données (mise en place d'un éditeur de trajectoire).

Améliorations possibles

Avec plus de temps, nous aurions pu :

  • Améliorer la détection des états. Le détecteur que nous fournissons détecte toujours un état connu (il n'y a pas d'état inconnu) car nous n'étudions pas toutes les caractéristiques de la courbe et des orientations et la détections des mouvements immobile,marche et course n'est donc pas assez restrictive. Il est envisageable des renforcer les conditions de détection d'état et de proposer à l'utilisateur quelle action il voulait représenter si l'on détecte un état inconnu.
  • Augmenter le nombre d'expressions. Pour l'instant, la marche, la course et le saut n'utilisent pour émotion que le fait d'être plus ou moins triste ou heureux. Quand à l'attaque, la peur est rajouté.
  • Augmenter le nombre d'actions différentes que le personnage virtuel peut effectuer. Actuellement, seul la marche, la course, le saut, l'attaque et la mort sont disponibles.
  • Nous pourrions étendre notre travail a des figurines non humanoïdes, comme un dragon ou un loup.

Bilan Personnel

Ce projet, mêlant informatique et mathématiques, a permis à chacun d'entre nous d'approfondir les notions qui l'intéressait. Nous avons été confronté à des problèmes d'ordre matériel qui nous poussait à imaginer des solutions pour les contourner. D'autre part, ce projet contrastait avec les autres que nous avons eu à faire car il nous a incité à travailler de manière autonome. Nous avons aussi pu avoir un avant-goût de ce que peut-être un travail de chercheur, et de s'insérer avec une équipe dans un projet déjà existant.

Références

[1] Thomas Jakobsen. Advanced character physics. Game Developers Conference, 2001.

[2] Guay, M., CANI, M.-P., and Ronfard, R. 2013. The Line of Action: an Intuitive Interface for Expressive Character Posing. ACM Transactions on Graphics 32, 6 (Nov.).

[3] Martin Guay : Sketchable Motion Abstractions for Character Animation, 2015, Submitted for publication

[4] Nikolaus F. Troje; Decomposing biological motion: A framework for analysis and synthesis of human gait patterns. Journal of Vision 2002;2(5):2. doi: 10.1167/2.5.2.

[5] F. Multon. Contrôle du Mouvement des Humanoïdes de Synthèse. PhD thesis, Université de Rennes 1, Octobre 1998.