Aide pour After Effect, techniques, expressions.

vendredi 19 février 2010

suivi d'objet

Animer un objet dans After Effect est une chose, animer une caméra en est une autre.
C'est parfois compliqué, fourbe, ingrat, ça fait pas ce qu'on veut, bref.




Nous allons en partie régler le problème pour un cas bien particulier, celui du suivi d'un objet dont le mouvement est important et plus ou moins chaotique, comme une mouche par exemple.

Nous allons essayer d'imiter le comportement d'un vrai caméraman.
Que fait le caméraman ? Il essaye tant bien que mal de suivre la trajectoire de l'objet pour le garder dans son cadre, en étant parfois en retard sur l'objet en question ou parfois un peu en avance s'il a anticipé.

Dans l'exemple, il s'agit d'un gros wiggle 3d bien large (le cercle fait 100 de large, le wiggle se déplace en tout sur 2000). Les particules sont là pour avoir un élément fixe pour que vous puissiez différencier le mouvement de la caméra et celui de l'objet.

Nous allons donc créer une expression sur le point ciblé de la caméra. La caméra en elle-même restera fixe.
Pour créer le caméraman parfait, il suffit logiquement de coller la position de l'objet au point ciblé par la caméra, et l'objet restera donc toujours au centre de l'image. Mais ça n'est pas ce que nous voulons faire.

Créez votre petite scène, et rajoutez un Null objet que nous appellerons "target cam". C'est à lui que nous allons appliquer l'expression et non à la caméra. Vous verrez un peu plus tard pourquoi.

Ajoutez 3 paramètres glissière à ce Null, et nommez les "amplitude", "tendance" et decalage".

amplitude définira le nombre d'images maximum de décalage dans le temps. (mettez 20 par exemple)
tendance nous permettra de définir si la camera sera plus souvent en retard ou en avance. (mettez -1 pour le moment)
decalage contiendra la valeur de décalage temporel à chaque image.

Écrivons l'expression dans "decalage" :

amp=effect("amplitude")("Curseur");
tendance=effect("tendance")("Curseur");

Expliquons un peut le processus avant de se lancer dans l'écriture de l'expression.
"decalage" va contenir un wiggle qui va faire varier le décalage temporel, selon l'amplitude.
Pour le moment, si on écrit juste "wiggle(0.2,amp), on va avoir une valeur qui va varier de -20 à 20, c'est à dire que la caméra sera (statistiquement parlant) autant en retard qu'en avance.
Nous allons donc utiliser la valeur "tendance" pour faire varier ça.
Ca va pas mal ressembler à la façon dont on a gérer le néon.
Quand tendance=1, la caméra sera toujours en avance.
Quand tendance=-1, la caméra sera toujours en retard.

si on le traduit pour le wiggle, ça donne ça :
tendance=-1 : le wiggle ira de -20 à 0 (avec un milieu à 10 donc)
tendance=0 : le wiggle ira de -20 à 20 (avec un milieu à 0 donc)
tendance=1 : le wiggle ira de 0 à 20 (avec un milieu à -10 donc)

Ecrivons donc cette amplitude qui peut aller de 20 à 40 selon les cas :


ampF=linear(Math.abs(tendance),0,1,amp,amp/2);

Voilà, on a bien ce qu'on voulait.
Maintenant, on doit créer la valeur final en décalant le centre du wiggle entre -10 et 10:

W=tendance*(amp/2)+wiggle(.2,ampF);

Ainsi, on transforme tendance pour qu'il varie de -10 à 10.
La dernière ligne, on transforme le nombre d'image en seconde :

d=thisComp.frameDuration*W;

Voilà, on a créé notre décalage en seconde.
Maintenant, écrivons l'expression pour la position de ce même calque (on est toujours dans "target cam" je vous rappelle):
p=thisComp.layer("target").transform.position;
d=effect("decalage")("Curseur");
p.valueAtTime(time+d);

J'explique même pas, vous connaissez par cœur tout ça. Juste une petite précision quand même.
Là j'ai écrit
time+d car c'est la valeur de tendance (de -1 à 1) qui va définir le retard ou l'avance.
-1, et le calque sera toujours en retard. 1, et le calque sera toujours en avance. Vous pouvez aussi mettre -10 ou 456, ça ne change rien, la fonction linear écrête les résultats.

Bref, on a bien notre calque "target cam" qui suit la même trajectoire que la cible, parfois en avance, parfois en retard (ça dépend de la valeur de tendance).

Occupons de la caméra à présent.
Nous allons comprendre pourquoi nous n'avons pas écrit cette expression directement dans la caméra.
Certes notre caméraman va être en avance ou en retard, mais il paraît fortement improbable que sa caméra suive exactement le trajet de l'objet.
Nous allons donc utiliser une fonction pour altérer cette trajectoire, et plus exactement, l'adoucir.

Nous allons utiliser la fonction smooth(a,b).
a est la durée sur laquelle un adoucissement va être effectué. Plus la durée sera longue, plus les creux et les bosses seront mélangés.
b Correspond à la puissance du smooth. Gardez comme règle de toujours utiliser une valeur impaire (C'est comme ça, discutez pas).

Sauf qu'il y a un petit problème...
On pourrait se dire qu'écrire ceci fonctionnerait :

P=thisComp.layer("target cam").transform.position;
P.smooth(1,5)

Et bien non, ça ne marchera pas !
Je vous explique pourquoi.
Les expressions dans After Effect ont leurs limites, et notamment une très importante:
Elles n'ont aucune mémoire. Vous ne pouvez pas enregistrer dans la mémoire d'AE une valeur qu'on va venir chercher à chaque image, ou bien faire que cette valeur augmente de n à chaque image (il y a en fait une technique pour faire ça, mais c'est un systeme D où AE recalcule tout depuis le début à chaque image).
Le problème est que se le smooth va faire une moyenne des valeurs, par exemple sur 1 seconde, il va aller voir les valeurs sur 1/2 seconde avant, et 1/2 seconde après. Sauf que voilà, il ne peut pas connaître les valeurs du futur puisqu'elles n'ont pas encore été calculée.

Nous devons donc passer par une étape supplémentaire.
Ecrivons donc juste ceci :
P=thisComp.layer("target cam").transform.position;

La caméra a donc exactement le même mouvement que "target cam"
A présent, en ayant la variable "point ciblé" sélectionnée, allez dans Animation, assistant d'image clé, convertir l'expression en image clés.

After va créer une clef par image calculées par l'expression, et désactiver cette dernière.

A présent, effacez l'expression et remplacez là par

smooth(1,5)

Et voilà !
Donc à chaque fois que vous faite une modification dans l'objet "target cam", vous devez reconnecter la caméra en remettant :
P=thisComp.layer("target cam").transform.position;

Refaire le coup de "Animation machin bidule), et remettre smooth(a,b).

Ne vous plaignez pas, ça prend 5 secondes.

Voilà c'est fini, vous avez votre belle caméra qui fait un beau mouvement.
Encore une article bien long, mais au final, il y a très peu de ligne à écrire.



Ce système est largement améliorable.
Par exemple, on peut imaginer que le caméraman dézoome quand la cible est trop loin de sa caméra.
Autre finesse, si l'objet se déplace essentiellement horizontalement, on voudra gérer de manière séparer X et Y.

Peut être pour un prochain article.

précision sur le néon

Certains se sont peut être demandé pourquoi j'ai créé un interrupteur qui va de 0 à 1 si c'est pour derrière l'utiliser uniquement en le transformant de 0,25 à 0,75.

C'est certes arbitraire, mais il y a plusieurs raisons:

Tout d'abord, une raison "logique". Étant donné qu'il s'agit d'un interrupteur, il est légitime qu'il aille de 0 à 1.
Ensuite, nous pourrions imaginer que cet interrupteur va agir sur d'autres éléments de l'animation, pour que ces derniers soient synchrones avec l'allumage du néon. Des valeurs allant de 0,25 à 0,75 seraient donc inappropriées d'un point de vue démocratique.

Enfin, et de manière plus générale, c'est un bon réflexe de créer ou de transformer les variables de 0 à 1 dans le cas où elles servent de potentiomètre ou d'échelle.
Nous avons déjà vu cela à plusieurs reprises:
Quand nous voulions avoir plusieurs calques qui se positionnent entre 2 calques et créent une chaine, nous avons utilisé leur index pour créer une variable allant de 0 à 1, et multiplier cette valeur par le vecteur allant d'un bout à l'autre pour les positionner. Plus génériquement, cela permet d'aller de 0 à la valeur finale d'une variable.

Les exemples sont nombreux, et souvent édifiants, car mathématiquement, c'est pratique :

-Pour la trigonométrie, on reste dans les valeurs du cosinus et du sinus.
-pour les vecteurs, on reste également à l'intérieur du vecteur sans le dépasser ni partir dans l'autre sens.
-Cela nous permet également de monter cette valeur à la puissance X, tout en restant entre 0 et 1, et ça, c'est extrêmement pratique.
Nous l'avons déjà vu par exemple pour l'amélioration des flares ou des lumières volumétriques. Au lieu d'avoir une droite qui va de 0 à 1, une courbe qui reste plus longtemps autour de 0 (avec une puissance supérieure à 1), ou au contraire on peut avoir une courbe qui va plus vite vers 1 (avec une puissance comprise entre 0 et 1 (non inclus). Pour rappel, la racine carrée correspond à une puissance de 0,5)

Nous verrons d'autres applications de l'utilisation de ce type de courbe pour casser la linéarité d'une valeur allant de 0 à 1.

jeudi 18 février 2010

animation modèle

Aujourd'hui, un petit truc tout simple, mais dont vous ne pourrez pas vous passer tant il peut vous faire gagner un temps précieux.




Imaginez que vous ayez créé une animation d'apparition pour un calque, qui mélange opacité, échelle, rotation, etc.
Vous êtes très content de votre mixture, ça fonctionne bien.
Sauf que cette apparition de luxe doit vous servir à faire apparaître 50 calques.
Alors oui, vous pourriez copier/coller toutes les clefs de chaque variable sur chaque calque, en faisant attention que les clefs soient bien synchro avec le début de chaque calque....
Et si vous changez votre animation, il faut tout refaire.
Bref, c'est tout pourri.

Nous allons donc dire à chaque calque de copier un calque modèle, chacun quand il le désire.

Imaginons comme dans l'exemple que vous ayez des clefs d'échelle et de rotation.
Ouvrez ces 2 propriétés dans votre 2ème calque, et écrivez ceci pour l'échelle par exemple :

target=thisComp.layer("modele");
e=target.scale;
e.valueAtTime(time-inPoint+target.inPoint)

C'est tout. Pas grand chose à expliquer.
Le valueAtTime nous permet donc de corriger le tir, et de faire en sorte que l'animation commence au bon moment, c'est à dire quand le calque apparait dans la composition.
Le +target.inPoint corrige également le tir si le calque modèle ne commence pas au début de la composition.

Il vous suffit de faire une expression équivalente pour chaque variable qui en a besoin, et tout sera automatisé.

vendredi 12 février 2010

faux contact

Un petit truc tout simple aujourd'hui, nous allons créer un interrupteur qui galère un peu à se stabiliser, à la manière d'un néon.



Il y a 1000 façons de voir les choses, en voici une.

Voilà commet nous pourrions décrire l'allumage d'un néon :
A partir du moment où l'on appuie sur l'interrupteur, plus le temps passe, plus le néon a une forte probabilité d'être allumé, jusqu'à ce qu'il le reste allumé sans probabilité de revenir à une position éteinte.

Nous devons donc créer une courbe qui restitue cette définition.
Nous allons utiliser une variable qui ira de 0 à 1.
A 0, le néon n'aura aucune chance d'être allumé, et à 1, le néon sera forcément allumé. Nous allons donc utiliser 2 outils : le wiggle, et Math.round. Vous pouvez cliquer sur un des 2 mots pour avoir un rappel de leur fonction.

Pour le moment, occupons nous du wiggle. Voilà la 1ere courbe que nous voulons :




Rappelez-vous qu'ensuite, nous allons utiliser Math.round pour que la valeur de sortie ne puisse faire que 0 ou 1. Donc tout ce qui sera sous 0.5 vaudra 0, et tout ce qui vaudra 0.5 et plus faudra 1.
Donc quand la variable vaut 0, notre wiggle ne doit rien produire au delà de 0.4999. Le wiggle faisant varier la valeur aussi bien vers le haut que le bas, nous avons donc besoin d'un départ à 0.25, et que l'amplitude du wiggle soit égale à 0.25, pour qu'on aille bien de 0 à 0.5.
Dans la théorie, je suis bien d'accord, cela donne une possibilité d'atteindre 0.5, mais dans la pratique, ça n'est pas le cas, le wiggle atteint très rarement sa valeur max.

Quand la variable vaudra 1, c'est tout pareil, mais entre 0.5 et 1.
Le wiggle va donc garder son amplitude de 0.25 de chaque coté, mais avec une valeur moyenne de 0.75.

Voilà ce que ça donne :

I=thisComp.layer("controle").effect("switch")("Curseur");

.25+ I/2+ wiggle(20,.25)

N'oubliez pas que le wiggle contient la valeur initiale de la variable ! N'oubliez donc pas de bien mettre 0 dans la valeur qui contient l'expression pour que le wiggle tremble bien autour de 0.

.25+ I/2 varie bien de 0.25 à 0.75. La fréquence de 20 est arbitraire, mais fonctionne bien.
Il ne reste plus qu'à utiliser le Math.round:

I=thisComp.layer("controle").effect("switch")("Curseur");

Math.round(
.25+ I/2+ wiggle(20,.25)
)

Regardez votre graphique. Plus on s'approche de 1, moins le néon a une chance de s'éteindre.
Une fois que la variable vaut 1, plus aucune chance que ça s'éteigne.

La morale d'aujourd'hui ? (oui j'ose)
Même si c'est intuitivement très simple, on se doute bien qu'il s'agit d'une histoire de Wiggle & Cie, il faut bien réfléchir en amont pour avoir l'effet escompté.