Rapport de projet

Animation par squelette d'un personnage

Thomas Gredin - Mathilde Perrot - Jérémy Haelewyn

UTBM - IN55 - 2018

Sommaire

  1. Présentation du sujet
  2. Analyse
    1. Tâches à réaliser
    2. Choix des outils à utiliser
    3. Analyse UML
  3. Conception
    1. Modélisation du personnage
    2. Conception de l’application
  4. Mode d'emploi
  5. Conclusion

  1. Présentation du sujet

Le sujet que nous avons choisi de traiter dans le cadre de l’UV IN55 concerne l’animation d’un personnage 3D. Il s’agit ici de modéliser un personnage sous un modeleur 3D et d'effectuer le rendu de plusieurs animations dans une scène.

L’objectif est de créer une application se basant sur OpenGL et permettant de parser un fichier de modèle contenant la position des vertices ainsi que les coordonnées de textures et les différents os formant le squelette d’un personnage. Le squelette permet de réaliser des transformations sur les vertices selon son influence sur ces derniers, c’est ce qui permettra de représenter les différentes animations.

Dans un premier temps, un personnage et des animations doivent être créés au coeur d’un éditeur de modèle. Dans ce projet nous utilisons Blender. Lors de la création des animations, le concepteur de modèle 3D créé des “Key Frames” qui définissent des transformations du squelette dans le temps. Pour animer le personnage dans notre application, nous devons donc interpoler au cours du temps entre ces différentes “Key Frames” pour déplacer les os et par extension les vertices qui y sont liés et obtenir un résultat fluide.

Ce rapport présente les moyens mis en oeuvre pour parvenir à un tel résultat.


  1. Analyse

Avant de commencer l’analyse il est important de définir les différentes tâches à réaliser pour les partager entre les membres du groupe de projet.

  1. Tâches à réaliser

On peut extraire les tâches à réaliser en créant un schéma qui va reprendre les différentes étapes et fonctions que le programme devra réaliser :

De cette vue globale on peut en tirer les fonctions sous-jacentes de l’application :

  • Lecture des données d’un modèle 3D à partir d’un fichier créé dans un format standardisé, contenant les informations sur les vertices, les os et les animations.
  • Donner la possibilité à l’utilisateur de choisir l’animation qu’il souhaite jouer parmi celles proposées.
  • Afficher le modèle, ce qui signifie qu’un moteur de rendu OpenGL doit être implanté.
  • Interpoler les positions des os du modèle entre les positions données dans le fichier du modèle et décrivant les animations. Cette étape permet de transformer les vertices pour obtenir une animation fluide.
  • Gérer une caméra libre. L’utilisateur doit pouvoir modifier l’orientation et la position de la caméra.

Maintenant que les tâches principales ont été identifiées, il faut déterminer les outils qui vont être utilisés.

  1. Choix des outils à utiliser

La création d’un tel programme nécessite plusieurs couches logiciels, dont certaines spécifiques à chaque système d’exploitation, parmi celles-ci on a :

  • La gestion de la fenêtre qui se chargera du contexte OpenGL ainsi que de l’affichage des buffers rendus côté processeur graphique.
  • La gestion des entrées utilisateurs (clavier, souris et éventuellement manette).

Cette couche logicielle est complexe à réaliser car elle nécessite la connaissance des API des systèmes d’exploitation que l’on souhaite supporter. Ainsi nous avons fait le choix de capitaliser le code écrit par d’autre en reprenant une librairie permettant de gérer les deux éléments cités ci-dessus.

GLFW est une libriairie mutli-plateforme qui nous permet de nous abstraire du système d’exploitation pour créer une fenêtre supportant les contextes OpenGL. Elle offre également le support des entrées souris, clavier et manette.

OpenGL n’étant pas une librairie mais une spécification ouverte et implémenté par les constructeurs de carte graphique, cela signifie qu’il faut récupérer les fonctions que l’on ne peut trouver sans les charger manuellement. Encore une fois les développeurs ont réalisés des bibliothèques permettant de réaliser cette tâche.

Nous avons choisis d’utiliser la librairie GLEW qui pour un minimum de contraintes nous apporte toutes les fonctions des dernières spécifications de OpenGL.

La suite de la conception de notre programme nous mène à l’affichage de modèles, cependant il faut charger ces derniers depuis un fichier contenant toutes les données sur ses vertices et textures.

Nous avons choisi d’utiliser la très célèbre librairie ASSIMP qui permet de charger un très grand nombre de formats différents de modèles.

Maintenant que nous possédons tous les outils nous pouvons réaliser le moteur de rendu qui permettra l’affichage de notre personnage animé.

  1. Analyse UML

La structure du projet est décrite dans le diagramme de classe présenté ci-dessous :


  1. Conception

  1. Modélisation du personnage

Nous avons décidé de réaliser un Minion, petit personnage jaune issu du film animé Moi, Moche et Méchant. Ce personnage fût réalisé sous Blender grâce à une suite de tutoriels.

Si on analyse brièvement la forme d’un Minion, on peut observer qu’il ressemble à une sorte de capsule avec des bras et des jambes. Il n’y a pas de distinction visible entre son cou et son tronc. Nous avons donc tout d’abord modelé son corps en forme de capsule.

Une fois la forme globale défini, les emplacements des bras et des jambes ont été défini et ceux-ci furent créés en forme d’octogone. Par la suite, nous avons ajouté ses mains et ses pieds, qui ne possèdent que trois doigts chacun.

La partie la plus complexe fût la modélisation du visage. Sur les différents clichés analysés avant la modélisation, nous avons constaté que sa bouche pouvait être très grande et que par conséquent elle devrait être profonde mais qu’il n’avait pas de lèvres. Par ailleurs, le Minion possède toujours une paire de lunettes englobant la totalité de ses yeux.

La modélisation du visage commença par ses yeux, qui ne sont que deux globes, puis par sa bouche : profondeur, langue, dents.Les lunettes furent modélisées une fois le visage terminé.

La modélisation fût terminée avec les lunettes. Mais pour donner de la vie à notre personnage, nous lui avons dessiné et attribué une texture.
Nous avons également conçu un rendu grâce aux matériaux mais par soucis de temps de développement, nous n'utiliserons que le rendu par texture dans le projet OpenGL.

Il ne nous restait ensuite plus qu’à créer le squelette et les animations.
De part sa forme humanoïde, son squelette se rapproche de celui d’un Homme. On retrouvera donc une colonne vertébrale, un bassin, un os pour son pseudo-cou, deux os pour contrôler l’orientation des yeux puis des os pour ses bras, ses jambes et ses doigts.

Concernant les animations, nous avons décidé d’en créer trois: sauter, marcher et courir. Celles-ci ont été réalisé par keyframe. Les animations marcher et courir n’impliquent le mouvement que des os, des bras et des jambes alors que celle du saut mobilise d’abord les jambes et les bras pour ensuite mobiliser le squelette entier.

Voici le rendu final de notre Minion. Par soucis de temps, les lunettes ne seront pas affichées ni dans notre démonstration, ni dans le projet.

  1. Conception de l’application

Création du moteur de rendu

Avant de s'intéresser aux animations il faut monter de toute pièce le moteur de rendu basé sur le standard ouvert OpenGL implémenté par les constructeurs de carte graphique (ex : NVidia).

Le moteur de rendu à été construit autour d’un système d’arbre de scène. Cette manière de faire permet de décrire une scène ou les éléments sont organisés de manière hiérarchique, ce qui va répercuter sur les transformations des éléments qui sont alors positionnés de manière relative à leur parent.

Le diagramme ci-dessous nous montre comment se construit cette organisation :

Dans cette architecture on observe plusieurs choses :

  1. La classe window possède une collection de “Scenes” qui représente un état différent de l’application, une logiques et un affichage différents.
  2. Chacune de ces “Scenes” possèdent une “Node” racine qui aura pour enfant toutes les autres nodes possédant chacune des éléments de logiques et de rendus différents.
  3. Chacune de ces Nodes peut à son tour posséder des Nodes enfants et les transformations sont calculés de manière récursive pour prendre en compte la transformation du parent et ainsi placer les Nodes enfants en fonction de leur parent.

Ainsi à chaque appel des fonctions render et update de la “Scene” active de la fenêtre, les fonctions update et render sont également appelés récursivement pour tout l’arbre de scène, c’est à dire pour toutes les “Nodes”.

Maintenant que la structure hiérarchique est mise en place il faut rendre les différents éléments en utilisant OpenGL. Pour se faire nous utilisons le système de “VertexArrayObject” ou nous stockons les données dans des “VertexBufferObjects”. Le schéma ci-dessous explique l’initialisation des “VertexArrayObjects” :

L’attribution de pointeur permet de stocker toutes les données dans un même buffer en spécifiant les décalages appropriés pour chacune des données à récupérer dans le programme shader.

Pour réaliser le rendu il suffit de lier le “VertexArrayObject” et d'appeler la fonction OpenGL de dessin d’éléments (glDrawElements(...)).


Gestion du squelette et des animations

Les mouvements du personnage sont gérés dans notre application par animation squelettale avec la librairie Assimp. Comme expliqué dans la partie précédente, un squelette et des animations associés au personnage ont été définis sous Blender. Assimp nous a permis d’extraire toutes ces données sous forme de Mesh (“peau” du personnage), Skeleton (squelette du personnage), Bones (Os, sous-ensembles du squelette) et Animations. Chaque vertex du modèle est associé à un ou plusieurs Bones, c’est ce qui va permettre un mouvement. Un poids est associé à un vertex sur un Bone, ce qui permet de déterminer l’importance de l’influence du Bone sur le vertex quand un mouvement est effectué.

La structure des Bones est ici hiérarchique, cela permet de déplacer un ensemble de Bones lors du mouvement d’un Bone parent. Les poids sont alors utilisés pour combiner les transformations de plusieurs Bones en une seule. En d’autres termes, la transformation d’un Bone est déterminée en combinant sa matrice de transformation avec celles de ses Bone parents, que l’on associe ensuite à ses poids.

Le Skeleton permet de gérer l’ensemble des Bones (mise à jour des transformations) en fonction des animations jouées (interpolation entre les “Key Frames”). Un noeud d’animation est associé à chaque Bone du Skeleton permettant de définir la position et la rotation interpolées en fonction de l’animation courante.


Gestion de la caméra libre

L’orientation de la caméra est gérée par un vecteur qui représente la direction de ce que l’on souhaite afficher à l’écran. Les coordonnées de ce vecteur orientation sont calculées grâce à la formule des coordonnées sphériques utilisant deux angles Phi et Theta qui représentent l’orientation sur l’axe horizontal et l’orientation sur l’axe vertical.

orientation.x = sin(Theta) * cos(Phi)
orientation.y = sin(Phi)
orientation.z = cos(Theta) * cos(Phi)

Phi correspond au mouvement de la souris sur l’axe Y et Theta le mouvement de la souris sur l’axe X selon une sensitivité fixée. Pour des raisons pratiques, l’orientation n’est mise à jour dans l’application que lorsque le bouton gauche de la souris est maintenu.

Le déplacement de la caméra est quant à lui géré par la mise à jour de sa position. Un déplacement vers l’avant est défini par une suite d’additions du vecteur orientation selon une vitesse fixée. A l’inverse, un déplacement vers l’arrière est une suite de soustractions du même vecteur.

Les déplacements latéraux permettent à la caméra de tourner autour du modèle. Une rotation est simplement appliquée autour de l’axe Y.

On définit également une cible qui correspond à la position de l’objet que l’on regarde à travers la caméra. La position de l’objet se définit simplement par l’addition entre la position de la caméra et son orientation. Ainsi, on peut facilement mettre à jour l’orientation de la caméra en fonction de ce que l’on souhaite cibler.


  1. Mode d’emploi

Pour contrôler les mouvements de notre personnage et de la caméra, nous avons créé des raccourcis clavier :

  • Q pour orienter le personnage vers la gauche
  • D pour orienter le personnage vers la droite
  • Z pour avancer dans l’espace
  • S pour reculer dans l’espace
  • F pour stopper la rotation
  • La souris + maintien clic gauche pour orienter la caméra dans l’espace

  • Shift pour lancer l’animation de saut
  • Espace pour lancer l’animation de course
  • Flèche du haut pour lancer l’animation de marche

  1. Conclusion

Ce projet nous a permis de mettre en pratique nos nouvelles connaissances acquises en OpenGL ainsi que de découvrir la modélisation et l’animation d’un personnage sous Blender.