Google+
3DVF Network :
ico_magazine
Magazine
ico_boutique
Boutique
ico_cgjobs
Portail Emploi
ico_upload
Hébergement d'image
ico_progiss
Progiss
Login
space
space

Accueil / Magazine / Didacticiels / L’animation temps réel dans les jeux vidéo, par David Lanier

space

L’animation temps réel dans les jeux vidéo, par David Lanier

Publié le 12 février 2009 par lieo
space

 2.3. Keyframes

 

C’est le moyen le plus commun de création d’une animation. Prenons un exemple concret :

Nous avons une caméra qui doit présenter deux combattants dans une arène, et nous souhaitons que la caméra fasse le tour de des 2 personnages en un temps de 5 secondes.

Remarque : On parle plus fréquemment en nombres de frames plutôt qu’en unité de temps. Pour passer d’un nombre de frames à un nombre de secondes on se sert de la vitesse d’affichage. Si on définit qu’une animation se jouera à 30 FPS sous le package 3D, on sait que 30 frames représenteront donc 1 seconde. Donc en faisant durer une animation 150 frames cela représentera 150 / 30 = 5 secondes d’animation. Mais en général le nombre de frames d’un jeu n’est pas constant…

On peut placer des clés d’animation sur la translation de la caméra, sa rotation ou son scale. Il y a d’autres possibilités que ces 3 cas mais nous n’en parleront pas ici.

Prenons par exemple la translation. Une clé d’animation contiendra au minimum un temps (ou une frame) et une translation qui est un vecteur 3D.

Les animateurs placent ces clés sous un package 3D. Le programmeur les récupère par une phase d’export du package 3D puis il créé l’animation dans le moteur du jeu.

Dans notre exemple, nous voulons que notre caméra passe d’une clé à une autre avec les informations que nous avons fixé dans chaque clé. La manière de passer d’une clé à une autre est appelé l’interpolation.

L’interpolation permet, à partir de 2 clés existantes, de calculer une valeur intermédiaire à un temps donné permettant de relier les 2 clés.

 

 

 


2.3.1. Algorithme général d’animation par keyframe

 

Il nous faut un tableau contenant toutes les clés d’animation. Chacune de ces clés contient un temps, celui auquel s’applique cette clé et une valeur pour la clé. Dans ce tableau les clés sont triées en fonction du temps par ordre croissant.

L’algorithme est le suivant :

 

-          On prend en entrée le temps courant. Notons le t.

-          On recherche entre quelles clés on se trouve dans le tableau de clés, pour cela on cherche à vérifier que t soit inférieur au temps d’une clé. On connaît maintenant la clé de départ et d’arrivée notons les respectivement Keyn et Keyn + 1. Appelons tn et t n + 1 respectivement les temps des clés de départ et d’arrivée.
Ces clés vérifient : tn <= t <= t n + 1.

-          Avec ces 2 clés nous pouvons utiliser une fonction d’interpolation pour nous renvoyer au temps courant la valeur de ce que nous interpolons (translation, rotation etc..)

 

Remarque : la fonction d’interpolation peut utiliser d’autres informations que ces 2 clés, elle peut utiliser d’autres clés pour lisser l’animation comme dans le cas du TCB.

Voyons quelques fonctions d’interpolation utilisées dans les jeux.

 

 

 

 

2.3.2. Interpolation de type linéaire

 

C’est le cas le plus simple. Nous allons traiter les clés 2 par 2 en considérant que nous avons une clé de départ et une clé d’arrivée  qui sont des translations. Pour passer de l’une à l’autre nous allons faire comme s’il existait une droite 3D entre ces 2 clés et suivre cette droite à vitesse constante.

Introduisons quelques notations afin de définir la formule d’interpolation linéaire. 

Soient Pn la translation de notre clé de départ, P n +1 la translation de la clé d’arrivée, tn le temps de la clé de départ et tn + 1 le temps de la clé d’arrivée. Pn et Pn + 1 représentent donc 2 points 3D.

Le segment de droite noté S entre nos 2 points 3D est donné par sa formule paramétrique S (t) qui est la suivante :

Pour t = 0, nous obtenons S ( 0 ) = Pn et pour t = 1, S ( 1 ) = Pn + 1. Et pour t dans ]0,1[ nous sommes sur le segment de droite dont les extrémités sont Pn et Pn + 1.

Voici un exemple de fonction en C++ qui prend en entrée un temps t et en sortie donne la nouvelle position en fonction des clés. Ce fragment de programme sera expliqué juste après.

 

 

 

struct Key

{

Vec3f Pos; //Vecteur 3D

float Time; //Temps de la clé

};

void SolveLinear(float t, Vec3f& NewPos)

{

Key* CurKey, *NextKey;

int i = -1 ;

const int NumKeys = TabKey.Count();

const int NumKeysMinusOne = NumKeys-1;

for ( i = 0 ; i < NumKeys ; i++ )

{

//Get current key

NextKey = TabKey[i];

if ( t < NextKey->Time )

{

//Get Cur Key

if ( i == 0 ) //We're on first key

CurKey = TabKey[NumKeysMinusOne]; //Prev key = Last key

else

CurKey = TabKey[i-1]; //Prev Key

//Translate time that is part of the interval [CurKey->Time , NextKey->Time]

//to be part of the interval [0,1] to use our formula

const float Coeff = ( t - CurKey->Time) / ( NextKey->Time - CurKey->Time );

assert(Coeff <=1.0f && Coeff >=0);

//Interpolate

NewPos = CurKey->Pos + Coeff * ( NextKey->Pos - CurKey->Pos );

break;

}

}

assert(i < NumKeys);//Have we found the good time ? We should…

}

 

 

 

Exemple de programme 1 : interpolation linéaire pour des translations




Explication de cet exemple de code :

Nous commençons par définir une structure appelé Key qui contiendra les informations d’une clé d’animation. Cette structure contient 2 attributs : Time qui est le temps de la clé et Pos qui est la valeur du vecteur 3D représentant la translation de la clé. Dans notre cas Vec3f représente un vecteur composé de 3 floats. Pour plus de précision on pourra utiliser le type double suivant la machine cible sur laquelle vous désirez jouer votre animation.

Voyons en détail la fonction SolveLinear :

elle prend en paramètre un float t qui représente le temps courant et une référence sur un Vec3f qui sera la translation résultante de notre interpolation. C’est-à-dire pour notre exemple, la translation qu’il faudra mettre dans notre caméra.

Nous allons dans cette fonction utiliser un tableau qui contient des pointeurs sur les clés d’animation de notre caméra. Ce tableau est appelé TabKey, il comporte une méthode Count qui renvoie le nombre d’éléments du tableau. Il contient aussi l’opérateur [ ]. TabKey[ i ] est le ième élément du tableau, soit la ième clé d’animation.

Les clés d’animation contenues dans le tableau sont triées par ordre croissant en fonction du temps de la clé.

Pour chaque appel de SolveLinear, nous recherchons à partir du temps courant noté t les clés de départ et d’arrivée, pour cela nous cherchons à vérifier la condition :


t < NextKey->Time


Lorsque celle-ci est vérifiée, nous savons que NextKey représente la clé d’arrivée et nous pouvons à partir de celle-ci retrouver la clé de départ.

Pour cela on évite le cas particulier ou NextKey se trouve être la première clé du tableau, dans ce cas on prend la dernière clé comme clé précédente. Si NextKey n’est pas la première clé, il suffit de prendre la clé précédente dans le tableau, c’est notre clé de départ.


C’est ce qu’effectue le test :


if ( i == 0 ) //We're on first key

CurKey = TabKey[NumKeysMinusOne]; //Prev key = Last key

else

CurKey = TabKey[i-1]; //Prev Key


A ce stade, nous connaissons les 2 clés de départ et d’arrivée, elles sont respectivement CurKey et NextKey.

Ensuite nous avons vu que pour utiliser notre formule d’interpolation sur un segment il nous faut un temps compris entre 0 et 1. Comme le temps courant est compris entre CurKey->Time et NextKey->Time, nous allons faire un changement de variable du temps courant pour le placer entre 0 et 1. En 0, le temps courant vaudra CurKey->Time, et en 1 notre temps vaudra NextKey->Time.


Ce changement est effectué dans le formule :

 

const float Coeff = ( t - CurKey->Time) / ( NextKey->Time - CurKey->Time );

assert(Coeff <=1.0f && Coeff >=0);


Après avoir vérifié que le temps courant était bien entre 0 et 1, il ne nous reste plus qu’à utiliser la formule d’interpolation pour calculer la nouvelle position :


NewPos = CurKey->Pos + Coeff * ( NextKey->Pos - CurKey->Pos );


Ensuite, il est inutile de parcourir les autres clés, c’est pourquoi on sort de la boucle avec l’instruction du langage C : break;

Exemple d’interpolation linéaire avec 6 clés :


 

Figure 2 : interpolation de type linéaire



Pour rentrer une vitesse de déplacement entre chaque segment, on se sert du temps de chaque clé. Si la différence de temps entre 2 clés est petite, la transition sera rapide, et au contraire, s’il elle est grande, la transition sera lente.

La trajectoire globale de notre caméra sera un ensemble de segments de droite ce qui n’est pas du plus bel effet... Nous souhaiterions avoir une trajectoire d’animation plus lisse...

Pour remédier à cela, on introduit la notion de tangente à l’intérieur d’une clé d’animation. On rajoute donc 1 ou 2 tangentes à chaque clé. Ces tangentes vont donner des informations complémentaires sur la manière dont on arrive ou dont on part d’une clé. Ces informations seront la vitesse et la “direction”.

Dans le cas ou l’on place 2 tangentes par clé, l’une servira à définir une manière d’arriver sur la clé l’autre servira à définir la manière dont on partira de cette dernière. On les appelle respectivement tangente in et tangente out. In pour “entrée sur une clé” et out pour “sortie de la clé”. Dans le cas où il n’y a qu’une tangente, elle sert à définir à la fois comment on arrive sur et comment on part de la clé.

Remarque : pour passer d’une rotation à une autre, on ne peut interpoler de la même manière que pour une translation. Il existe cependant des moyens de faire ces interpolations, par exemple, quand les rotations sont sous forme de quaternions, on peut utiliser la fonction SLERP (spherical linear interpolation) qui fait une interpolation de type linéaire entre 2 quaternions à un temps donné entre 0 et 1. Pour information la formule du SLERP est :


( p sin ( (1-t) A p q ) + q sin( t A p q ) )

SLERP ( t ; p ,q ) = __________________________

sin ( A p q )

Où p est le quaternion de départ, q celui d’arrivée, t le temps compris entre 0 et 1 et A p q est l’angle entre p et q.

Pour plus de détail sur les quaternions et les différents types d’interpolation, nous invitons le lecteur à regarder

[ 5 ] et [ 8 ].

 


 

 

2.3.3.   Tangentes et courbes d’animation

 

De manière pratique, ces tangentes peuvent être édités sous le package 3D utilisé pour créer les clés. Cette tangente est de même nature que la valeur de la clé.

Par exemple :

 

-          Dans une clé sur la translation de notre caméra, la valeur de la clé sera une translation représentée par un vecteur 3D. De ce fait, les tangentes seront elles aussi des vecteurs 3D.

-          Dans une clé sur la rotation de notre caméra, la valeur de la clé sera une rotation (par exemple représentée par un quaternion). Donc les tangentes seront elles aussi des rotations !

 

Remarque : Ceci paraît sans doute assez compliqué. Il est assez intuitif de se représenter une tangente pour une translation, c’est un vecteur 3D, on peut le visualiser dans l’espace. Mais il est peu intuitif de se représenter une tangente pour une rotation...

On peut voir cette tangente comme la manière d’arriver sur la clé ou de repartir de cette dernière, c’est donc une rotation qui représente ici cette manière dans le cas d’animation sur la rotation.

 

En pratique : Est-ce que les animateurs rentrent une valeur de vecteur ou de quaternion pour les tangentes d’une clé ?

Non. Les animateurs règlent leurs tangentes sur ce que l’on appelle une courbe d’animation.

 

Exemple : Lorsque l’on anime la translation d’un objet, on peut voir une courbe d’animation sur chaque composante de cette translation soit x, y et z.

la figure suivante montre une courbe d’animation sur la composante z d’une translation:

 


 

Figure 3 : Une Courbe d’animation


 

 

Cette courbe d’animation comporte 3 clés qui sont représentées par les carrés noirs aux extrémités et le blanc au milieu. La courbe joignant les 3 clés est ce que l’on appelle la courbe d’animation. Sur la 2ème clé, on peut visualiser, 2 tangentes In et Out. Les animateurs savent interpréter ces courbes et régler les tangentes pour obtenir l’effet qu’ils désirent obtenir entre 2 clés.

Dans certains cas, plutôt que d’éditer directement les tangentes sur la courbe, il est possible de rentrer certains paramètres qui seront utilisés pour reconstruire les tangentes avec l’aide des clés voisines.

 

Par exemple : une clé avec un type d’interpolation en TCB contiendra au lieu de tangentes, les paramètres : tension , continuity, bias. Nous allons étudier par la suite ce type d’interpolation.

L’interpolation de type Hermite est une interpolation qui fait intervenir des valeurs de clés, et des tangentes. Dans le cas le plus général d’Hermite, on ne prend en compte qu’une tangente par clé.

 


 

Figure 4 : Une courbe d'Hermite

 

 


Explication de la figure précédente :

Sur cette courbe d’Hermite, on a 5 points notés de P0 à P4, en chacun de ces points part une tangente avec en son extrémité une flèche, les tangentes dont notées de T0 à T4. On voit que la courbe résultante passe par tous les points.

 

 

 

 

2.3.4.  Interpolation de type Hermite

 

L’interpolation de type Hermite cubique se fait à l’aide de 4 polynômes de degré 3 notés H0, H1, H2, H3. Nous interpolons toujours entre 2 clés, appelons Pn la valeur de la clé de départ, et Pn+1 celle de la clé d’arrivée. Tn et Tn +1 représentent respectivement la tangente à la clé Pn et celle sur la clé Pn +1.

La formule d’interpolation d’Hermite est la suivante :

Avec les polynômes (appelés « Blending Hermite functions ») :


H0 = 2t3 - 3t2 + 1

H1 = -2t3 + 3t2

H2 = t3 - 2t2 +t

H3 = t3 - t2

 

 

Pour trouver pourquoi les polynômes ont ces valeurs, il suffit de prendre la forme générale d’un polynôme de degré 3 : Hi(t) = ai t3 + bi t2 + ci t +di , de remplacer dans la formule précédente les H i(t) i = 0 à 3 par cette forme générale. Puis de poser que P(0) = Pn, P(1) = Pn + 1 et P’(0) = Tn et enfin P’(1) = Tn + 1 puis de résoudre le système linéaire résultant. Nous n’aborderons pas le détail ici.

Le cas qui nous intéresse est un cas particulier des courbes d’Hermite, ce sont les splines de Kochanek-Bartels appelées aussi plus couramment splines TCB.

Pour ces splines, les tangentes sont calculées à partir des paramètres T, C et B et de 4 clés ! Ces paramètres T, C et B doivent tous être compris entre –1 et 1 pour la formule suivante.

Nous avons déjà Pn et Pn + 1 clés de départ et d’arrivée, notons la clé précédant celle de départ Pn - 1 et la clé suivant celle d’arrivée Pn + 2.

Avec les notations précédentes, nous pouvons calculer les tangentes :

 

 

(1-T)(1+B)(1-C)                   (1-T)(1-B)(1+C)
Tn =  ______________ * (Pn – Pn - 1)  +  _____________ * (Pn - 1 – Pn)
             2                                  2

et

(1-T)(1+B)(1+C)                   (1-T)(1-B)(1-C)

Tn + 1 =  ______________ * (Pn - 1 – Pn)  +  _____________ * (Pn + 2 – Pn + 1)
             2                                  2

 

Ce qui nous permet de calculer P( t ).

 

 

 

Explication de l’influence des paramètres TCB :

-          T : tension, sert comme son nom l’indique à régler la tension de la courbe, plus T sera proche de 1 plus on se rapprochera d’une ligne droite entre les 2 clés, plus T sera proche de –1 plus la courbe sera lâche. On peut voir cela comme quelqu’un qui tire sur une corde (valeur 1) ou la relâche (valeur –1).

-          C : continuity, sert pour définir comment le changement de vitesse et de direction interviendra entre le moment ou l’on entre sur une clé et le moment ou l’on part de cette clé.

-          B : bias, définit la direction de la courbe dès qu’elle passe la clé.

Il y a des cas particuliers des splines TCB lorsque :

-          T = C = B = 0, on appelle cette interpolation spline de Catmull-Rom.

-          B = C = 0, on l’appelle spline Cardinal.

-          T = C = 0, on l’appelle “Bias controlled spline”

-          T = B = 0, on l’appelle “Continuity controlled spline”

Voyons un exemple de code qui concrétise cela. Dans cet exemple nous avons appelé Pn - 1 PrevKey,  Pn CurKey, Pn + 1 NextKey et Pn + 2 NextNextKey.

 

 

 

 

 

void SolveTCB ( float t, Vec3f& NewPos)

{

Key *NextKey, *NextNextKey, *CurKey, *PrevKey;

const int NumKeys = TabKey.Count();

const int NumKeysMinusOne = NumKeys-1;

for ( int i = 0 ; i < NumKeys ; i++ )

{

//Get next key

NextKey = TabKey[i];

if ( t < NextKey->Time )

{

//Get Current Key

if ( i == 0 ) //We're on first key

//First key = Last key, so the prev is the one before the last

CurKey = TabKey[NumKeysMinusOne-1];

else

CurKey = TabKey[i-1]; //Prev Key

//Get Previous Key

if (i == 0)

PrevKey = TabKey[NumKeysMinusOne-2];

else

if (i == 1)

PrevKey = TabKey[NumKeysMinusOne-1];

else

PrevKey = TabKey[i-2];

//Get NextNextKey

if (i == NumKeysMinusOne)

NextNextKey = TabKey[0];

else

NextNextKey = TabKey[i+1];

//Interpolate

const float Coeff = ((t-CurKey->Time) / (NextKey->Time - CurKey->Time));

//Update values from TCB

const float OneMinusTension = (1.f - CurKey->tension);

const float OneMinusContinuity = (1.f - CurKey->continuity );

const float OnePlusContinuity = (1.f + CurKey->continuity );

const float OneMinusBias = (1.f - CurKey->bias);

const float OnePlusBias = (1.f + CurKey->bias);

//Compute tangents

Vec3f TanCurKey = 0.5f*((OneMinusTension*OneMinusContinuity*OneMinusBias*( NextKey->Pos - CurKey->Pos )) +

(OneMinusTension*OnePlusContinuity*OnePlusBias* ( CurKey->Pos - PrevKey->Pos )));

Vec3f TanNextKey = 0.5f*((OneMinusTension*OnePlusContinuity*OneMinusBias*( NextKey->Pos - CurKey->Pos )) +

(OneMinusTension*OneMinusContinuity*OnePlusBias*( NextNextKey->Pos - NextKey->Pos )))

//Compute new position

NewPos = H0(Coeff) * CurKey ->Pos;

NewPos += H1(Coeff) * NextKey->Pos;

NewPos += H2(Coeff) * TanCurKey;

NewPos += H3(Coeff) * TanNextKey;

break;

}

}

}

 

 

 

Exemple de programme 2 : interpolation TCB pour des translations


 

 

Ce fragment de code est assez similaire au précédent dans le cas de l’interpolation linéaire.

L’algorithme global reste le même, seuls les cas particuliers sont plus denses car il nous faut prendre une clé avant la clé courante et une clé après la prochaine. Il y a donc des cas particuliers quand nous sommes au début ou à la fin des clés dans le tableau.

 

Remarque : dans le cas des rotations sous forme de quaternions, pour l’interpolation linéaire de 2 rotations, nous avons vu que nous pouvions utiliser la fonction SLERP. Dans le cas de l’interpolation de type TCB entre 2 rotations, nous pouvons utiliser la fonction SQUAD (spherical cubic interpolation qui représente une interpolation de type bilinéaire sur un quadrilatère). Cette fonction prend en entrée 2 quaternions, un de départ et un d’arrivée, 1 tangente pour chaque clé sous forme de quaternions et un temps compris entre 0 et 1. Elle réalise l’interpolation de type TCB.

 

Pour information, la formule du squad est :

 

Squad ( t; p, a, b, q ) = Slerp( 2t (1 – t); Slerp(t; p, q), Slerp(t; a, b) )

Où t est le temps entre 0 et 1, p est le quaternion de départ, q celui d’arrivée, a le quaternion tangente de départ et b celui d’arrivée.<

space
space

Les derniers commentaires (0)

Soyez le premier à déposer un commentaire.
space
space
space
space
space
A Propos | Contact | Publicité
Copyright © 2000-2018 3DVF. Tous droits réservés. | Infogérance serveur | tracker
space