Aide pour After Effect, techniques, expressions.

vendredi 24 juin 2011

Sélecteur graphique

De retour après pas mal de temps, j'ai donc pris le temps d'écrire ce nouveau post, qui ouvre une toute nouvelle dimension à l'animation; nous allons développer un outil assez puissant.

Je souhaite développer une idée très utilisée dans beaucoup de domaine : le sélecteur graphique. De quoi s'agit-il ? C'est très simple, le principe est de traduire une ligne droite en courbe. Tout le truc est évidemment de choisir la courbe que l'on souhaite.

Nous avions déjà vu ce principe sur de nombreux posts, j'en ai déjà parlé en long en large et en travers.
L'idée était d'utiliser une variable qui va de 0 à 1 (et linéaire), et d'altérer cette valeur pour en modifier la progression, selon ce tableau récapitulatif :



Maintenant, imaginons que nous voulons créer notre propre courbe, sans être bloqué par ces courbes mathématiques. Ensuite, nous allons nous en servir pour dispatcher des valeurs différentes à différents calques.
Nous allons utiliser la fonction valueAtTime.
Il y a une infinité d'applications, je vais donc en développer quelques unes.

Mais voyons déjà la structure.

Créez un Null avec un paramètre glissière.
Appelez le "courbe".
Allez dans courbe,, et animez la valeur, en mettant une clef à l'image 0 égale à 0, et une clef à 1 seconde égale à 1. Le choix de faire une courbe sur une seconde est arbitraire, mais pratique car on va de 0 à 1, et cela permet de ne pas se tromper entre chaque projet. "Comment j'ai fait ma courbe ce coup-ci déjà ?".
Passez cette ligne en easeInOut (F9), et dessiner la courbe qui vous plait.

Créez un petit carré, placez le en bas à gauche, et animer le sur X pour qu'il se déplace de gauche à droite.
On va dire qu'il se déplace sur X de 50 à 200 par exemple.

Ouvrer l'expression de la position, et écrivez :

courbe=thisComp.layer("Nul 1").effect("courbe")("Curseur");
hauteur=-200;

Quand le carré sera sur x=50, le cube sera en bas. Quand il sera à x=200, Y montera de 200 (hauteur = -200 car Y=0 est en haut).
Ensuite :

graphTime=linear(value[0],50,200,0,1);

Ici, on définit notre sélecteur, qui va nous permettre de prendre la valeur qui nous intéresse sur la courbe. Le 0 et le 1 de la fin correspondent donc au temps.

graph=courbe.valueAtTime(graphTime);

On a donc créer une valeur graph qui va prendre la courbe à un moment défini par la valeur de X.
La dernière ligne :

[value[0] , graph*hauteur+300]

Le graph va donc jouer son rôle d'interrupteur pour la hauteur, et 300 nous permet juste de mettre le carré vers le bas.
Voyez l'animation. Quand le carré avance, il monte en suivant une trajectoire qui ressemble à votre courbe.

Certains d'entre vous ont peut être une sensation de déjà-vu, puisqu'au final, on aurait pu dans ce cas utiliser simplement un linear(value,a,b,x,y) pour faire varier Y selon X, mais nous n'aurions pas eu la maîtrise des valeurs intermédiaires.

Ca n'a pas l'air comme ça, mais l'outil est surpuissant. Changer la forme de la courbe, et l'animation du carré changera sur Y.
L'outil est particulièrement pratique pour gérer plusieurs calques à la fois.
Un exemple que je ne vais pas développer mais qui parait évident : Faire un coverflow.

Car toute la question du coverflow est : de quelle façon mes calques s'avancent, reculent, grandissent. Vous pouvez ainsi tout contrôler facilement. Les calques ont une variable qui définisse leur distance par rapport au centre du coverflow, et la courbe vous permet de les faire commencer à grandir tout doucement au début, quand ils sont encore loin, puis de les faire grandir d'un coup, pour que celui du centre ait une taille bien plus grande que ceux d'à côté.

Ces applications pourraient être définies comme directes. En effet, l'utilisation de la courbe se retrouve plus ou moins visuellement dans l'animation, ici en trajectoire.

Mais on peut aller bien plus loin que cela, en créant par exemple des populations différentes. Soyons chiants 2 secondes, cela est comparable aux courbes que vous pouvez voir dans des magazines économiques par exemple. On vous montre une courbe des revenus moyens, et de la population qui est dans cette tranche. Résultat, la courbe est au plus haut sur le salaire médian, et très basse aux 2 extrémités.

Nous allons voir un exemple où l'on fait un peu la même chose, mais en moins chiant, pour ceux qui sont restés, merci à eux.

Nous allons reprendre l'animation des mouches que nous avons vu ici.
Les mouches suivent donc en retard, nous allons dessiner ce retard.

Voici ce que j'ai écrit pour chaque mouche :

target=thisComp.layer("Orange uni 1").transform.position;
courbe=thisComp.layer("Nul 1").effect("courbe")("Curseur");
retard=thisComp.layer("Nul 1").effect("retard")("Curseur");

seedRandom(index,true);
R=random(0,1);
graph=courbe.valueAtTime(R);

W=wiggle(2,10);

target.valueAtTime(time-graph*retard)+W-value

On comprend donc grace au random que chaque mouche va se voir attribuer une valeur aléatoire sur la courbe entre T0 et T1.

1er exemple :





Avec cette courbe, étant donné que la distribution des randoms est homogène entre 0 et 1, on comprend que les mouches auront plus de chance d'avoit une valeur proche de 1 que de 0.

On obtient donc cette animation :



La plupart des mouches ont un retard important.

2ème courbe :





Ici c'est l'inverse, la plupart des mouches auront un retard proche de 0, sauf certaines qui auront une valeur aléatoire attribuée dans la partie droite de la courbe.




En gros, nous avons créé un linear(value,a,b,x,y) beaucoup plus puissant, car nous contrôlons les valeurs intermédiaires.

Voilà, à vous de trouver comment cela peut vous servir. Le dernier exemple est assez parlant, car la simple utilisation du random aurait donné une distribution très homogène. Ici, on a pu créer un vilain petit canard très en retard.
Il vous suffit de changer la forme de la courbe pour voir toutes les mouches changer de position en prendre en compte la nouvelle distribution des retards.

Les applications sont infinies, et permettent de grandes finesses d'animation.

vendredi 18 février 2011

composition 2d en 3d

Oui je sais, une éternité sans post... C'est ainsi.
Petite astuce du jour. Vous avez une photographie, vous avez découpé différentes parties (1er plan, 2ème plan...), et voulez bouger votre caméra pour que l'on s'aperçoive que les différents éléments de la photo sont sur des plans différents.

Comme ça, ça n'a pas l'air bien compliqué, le truc, c'est que si on place les plans sur différentes valeurs de Z, ils n'auront plus le bon rapport de taille entre eux. Nous allons remédier à cela.

Comme tous ces calques seront en 3d, et que cette impression de 3d ne va apparaître que lorsque la caméra va se déplacer, il faut commencer par orienter nos calques dans le cas où la caméra ne serait pas face aux calques :

Donc dans l'orientation des calques :

a=thisComp.layer("Caméra 1").transform.position.valueAtTime(0);
lookAt(position,a)

Maintenant, pour pouvoir déplacer les calques sur l'axe Z de la caméra :
Créer un paramètre glissière sur le calque que vous nommerez "distance camera"

Puis, dans la position :

cam=thisComp.layer("Caméra 1").transform.position.valueAtTime(0);
V=sub(position,cam);
dist=effect("distance camera")("Curseur")/100;
value+V*dist

J'ai déjà expliqué tout ça, donc je ne m'attarde pas. Juste au sujet de "dist", j'ai divisé la valeur par 100 car notre vecteur doit être multiplié par 1 ou -1 pour que le calque soit collé à la caméra ou 2 fois plus loin. Le paramètre glissière sera ainsi plus simple à utiliser, et ça va nous aider pour l'échelle.

Voilà, vous pouvez tester, votre calque se déplace bien sur l'axe de la profondeur de la caméra quand vous modifiez le paramètre glissière.

Passons à l'échelle :
Quand le paramètre glissière est à 0, notre objet est à la bonne taille, son échelle est donc bien de 100. Si notre calque est collé à la caméra, il doit théoriquement avoir une échelle de 0. Si le paramètre glissière vaut 100, le calque sera donc 2 fois plus loin, il doit donc être 2 fois plus gros, etc.

On a donc :

dist=effect("distance camera")("Curseur");

s=dist+100;
[s,s,s]

Et voilà, c'est tout.

mardi 18 mai 2010

accordeon 2



Tout d'abord, cet article est destiné à ceux qui ont lu (et compris) l'article précédent sur l'accordéon, car je m'y réfère.


Voyons à présent la version 2 de l'accordéon, avec un retard sur chaque plaque, dans un sens où dans l'autre.

Imaginez que vous tenez ses plaques dans les mains et que vous lâchiez toutes les plaques sauf la 1ere. Elle vont toutes tomber en paquet, et se défaire en partant du haut, au fur et à mesure que la résistance de la 1ere plaque fasse son effet. Ca c'est le cas où les plaques vont s'ouvrir en partant de la 1ere plaque.
L'autre cas est si vous poser les plaques sur une table, et que vous tirer la dernière plaque. C'est cette dernière qui se dépliera en 1ère, les autres suivants. L'inverse donc.
Nous allons donc prévoir ces 2 possibilités.
Cette option nous impose un changement de méthode.
Dans la version précédente, nous utilisions l'angle de la plaque précédente pour trouver l'angle de la plaque courante. Si nous voulions on retard, il suffirait d'utiliser une phrase du type :

ThisComp.layer(thisLayer,-1).rotationY.valueAtTime(time-d);

Le problème, c'est que l'on veut pouvoir avoir le retard dans un sens où dans l'autre. Cela veut dire que d va donc varier selon le sens, mais de manière compliquée, puisque la 1ère plaque à démarrer ne sera pas la même, et que tout va s'inverser....Vraiment pas pratique.

Nous allons donc utiliser une autre approche, dont vous connaissez la base.
Nous allons utiliser l'index relatif de chaque calque pour connaître sa position dans la liste, et en définir son propre retard.
Nous aurons donc la structure que vous connaissez:

-controle
-plaque 1
-plaque clone 1
-plaque clone 2
-plane clone n
-sol

Commençons par le calque "controle", et voyons ce dont on a besoin en plus:

-random
-angle
-inverse
-sens
-retard
-courbe

Seuls les 3 premiers viennent du 1er accordéon, les 3 autres sont nouveaux.
-sens va nous dire qu'elle est la 1ère plaque à démarrer (la 1ère ou la dernière de la liste). Pour cette valeur, vous pouvez d'ailleurs copier l'effet "inverse", puisqu'on veut également une valeur qui aille de -1 à 1.
-retard sera le retard en nombre d'image entre chaque plaque (environ, vous verrez pourquoi)
-courbe va nous permettre de ne pas avoir un retard linéaire.

Créez donc également votre null "sol" qui sera sous toutes les plaques dans la timeline.

La différence se situera au niveau de la rotation sur Y, l'expression sur la position ne change pas.
Commençons par l'expression de la 1ere plaque, celle qui est différente des autres:

angle=thisComp.layer("controle").effect("angle")("Curseur"); H=thisComp.layer("controle").effect("random")("Curseur");
seedRandom(index,true);
hasard=random(-H,H);
inverse=thisComp.layer("controle").effect("inverse")("Curseur"); sens=thisComp.layer("controle").effect("sens")("Curseur"); nombre=thisComp.layer("sol").index-thisComp.layer("carton_start").index; retard=thisComp.layer("controle").effect("retard (frame)")("Curseur")*thisComp.frameDuration;

Les rouges sont les nouveaux venus.
nombre vaut bien le nombre de plaques.
Nous allons à présent définir la position de cette plaque, selon la valeur de sens.
Elle sera ou en 1ère position, ou en dernière:

pos=0;
if (sens==-1) {pos=nombre};

Quand sens=1 (le "==" est le égal dans le monde des conditions. Le simple "=" enregistre une valeur dans une variable, à ne pas confondre), pos=0, ça sera le 1er calque.
Quand sens=-1, il sera en dernier, pos vaudra nombre+1=le nombre de plaques, car nous avons définit la valeur de nombre -cette plaque.
Bref, la dernière ligne :

(angle.valueAtTime(time-retard*pos)+hasard)*inverse;
Rien de bien compliqué. On comprendra donc que le retard vaudra ou 0, et bien retard*le nombre de plaque, et sera donc bien en dernier.

Passons à présent aux autres plaques, plus compliqué. En effet, ça sera moins binaire (ou 1er ou dernier).

Nous allons créer une variable "retardMax" qui sera égale au retard entre la derniere plaque et la 1ère. Puis, chaque plaque aura une position de 0 à 1 (dans un sens ou dans l'autre justement), et se verra appliqué un retardMax*cette position.
Allons y:

angle=thisComp.layer("controle").effect("angle")("Curseur"); H=thisComp.layer("controle").effect("random")("Curseur");
seedRandom(index,true);
hasard=random(-H,H);
inverse=thisComp.layer("controle").effect("inverse")("Curseur"); nombre=thisComp.layer("sol").index-thisComp.layer("carton_start").index; retardMax=thisComp.layer("controle").effect("retard (frame)")("Curseur")*thisComp.frameDuration*nombre; sens=thisComp.layer("controle").effect("sens")("Curseur"); courbe=thisComp.layer("controle").effect("courbe")("Curseur");
Tout pareil que la 1ère plaque, on a juste rajouté courbe.
Notez également une différence pour le retard. Cette fois-ci, la variable vaut la totalité du retard.

direction=1;
if (thisComp.layer(thisLayer,-1).rotationY<0 0="" color:="" direction="-1}; <br /> <br /><span style=" rgb="">Direction fera en sorte que chaque plaque soit dans le sens opposé à la plaque précédente.

posBase=index-thisComp.layer("carton_start").index+1;
posSens=posBase;

posSens est la valeur qui nous allons utiliser à terme. Quand sens=1, on ne change rien, on garde l'ordre initial.

if (sens==-1) { posSens=nombre-posBase+1};

Quand sens=-1, c'est l'inverse.
Reprenons notre exemple de 5 plaques. Pour la 5ème:
posSens=5-5+1=1
Pour la 4ème:
posSens=5-4+1=2

On est bon, ça marche !

retardN=linear(posSens,1,nombre,0,1);

On applique maintenant la transformation de la valeur pour aller de 0 à 1. Le N de retard vaut pour Normal, car voici la ligne suivante:

retardCourbe=Math.pow(retardN,courbe);

Cela nous permet d'avoir une retard exponentiel ou logarithmique. Dans le calque controle, la valeur de "courbe" ira de 0 à l'infini, mais dans la pratique, on ira pas au delà de 10, ça ne sert à rien.
On se refusera d'ailleurs les valeurs négatives. Pour une puissance, c'est bof. Pour éviter les messages d'erreurs et les désactivations massives d'expressions, nous allons empêcher "courbe" de prendre des valeurs négatives.
Ecrivez ça dans "courbe" du calque de controle :

Math.abs(value)

Ca évitera les désagréments.
Revenons à la rotationY de notre plaque. La dernière ligne:

(angle.valueAtTime(time-retardMax*retardCourbe)+hasard)*inverse*direction;

C'est long, mais pas compliqué :
on multiplie le retardMax par la valeur qui va de 0 à 1. Le tout multiplier par inverse et direction pour le sens des plaques.

Et voilà, c'est terminé.
C'est encore améliorable, car il y a une faiblesse:

Si vous mettez une valeur trop grande dans le random, vous aurez d'une part des calques qui se chevauchent au départ, et on peut également avoir des sautes de plaque qui passe d'un angle positif à un angle négatif, vous comprendrez vite pourquoi.
Donc restez raisonnable !

samedi 15 mai 2010

Yvette et son accordéon, et son orchestre

Me revoici avec de nouvelles petites idées, aujourd'hui, nous allons voir comment faire un système d'accordéon:



L'idée, comme d'habitude, est de rendre le maximum de choses automatiques. La seule variable que nous allons animer sera l'angle d'ouverture.
La base est très simple, mais c'est en affinant le travail que ça va un peu se complexifier, juste un peu...
Commençons par faire une version toute simple, où toutes les plaques tournent en même temps.
Allons y :

Commençons par créer notre éternel calque Null de controle, et ajoutons y quelques paramètres glissière:
-random (pour que l'angle entre chaque plaque puisse différer)
-angle (l'angle général)
-inverse ( nous permettra d'inverser le sens de chaque plaque)

Dans tous les cas, la 1ere plaque sera différente des suivantes, commençons par celle là:
Créez votre calque, des dimensions que vous souhaitez, passez le en mode 3d, et écrivez ceci dans sa rotation Y:

angle=thisComp.layer("controle").effect("angle")("Curseur");
H=thisComp.layer("controle").effect("random")("Curseur");
inverse=thisComp.layer("controle").effect("inverse")("Curseur");
Pas de fourberie pour le moment. Créons à présent la variable random:

seedRandom(index,true);
hasard=random(-H,H);

La dernière ligne :

(angle+hasard)*inverse;
Et voilà. La multiplication par inverse nous permet d'inverser le sens de la 1ere plaque, et de toutes les suivantes comme nous le verrons plus tard. Ha oui, j'oubliais, il faut qu'on s'occupe du point d'ancrage ! En effet, nous ne voulons pas que le calque tourne en son centre, mais sur sa tranche. Ecrivons donc ceci dans le point d'ancrage :

[0,value[1],0].

On pourrait faire ça sans expression en mettant 0 pour X, mais ainsi, si on change la taille du calque, la valeur en Y restera centrée. On sait jamais...

A ce propos, nous comprenons donc que la valeur inverse devra être égale à 1 ou -1. Comme c'est pas très pratique de rentrer ou 1 ou -1 (on est obligé de passer par le clavier pour rentrer la valeur), on va faire en sorte de que la valeur soit rentrable à la souris. Nous allons faire en sorte que lorsque la valeur est positive, elle vaut 1, et quand elle est négative, elle vaut -1.
Ouvrez donc l'effet "inverse" de votre calque de controle, et écrivez ça dedans :

inverse=1;
if ( value<0 inverse="-1">
Et voilà, ainsi, il suffit de faire glisser le curseur vers le positif ou le négatif pour avoir la valeur voulue.

Passons à présent à la 2eme plaque, qui sera un clone de toutes les suivantes. Il s'agit bien entendu d'un calque de taille équivalente ( à priori). Même opération pour le point d'ancrage !
Nous allons commencer par nous occuper de la position de cette plaque, qui est fonction de la plaque précédente. Un petit schéma vu de haut va nous éclairer:


















Et oui, vous reconnaissez votre ami la trigo. Le trait rouge est la 1ere plaque, le trait bleu est la 2eme. Quand l'angle de la plaque rouge change, on comprend que le point P de la plaque bleu va se déplacer sur le cercle trigo. La taille du cercle sera donc fonction de la taille du calque précédent.

Ouvrez sa position, et écrivez :

papa=thisComp.layer(thisLayer,-1);
angle=degreesToRadians ( papa.rotationY);
depart=papa.position; taille=papa.width;

Nous avons ainsi défini à la 1ere ligne la plaque référence, c'est à dire le calque juste au dessus.
Nous avons donc besoin de l'angle qu'il prend, sa position, et sa taille (respectivement l'angle sur le cercle, les coordonnées du centre du cercle et le diamètre du cercle.)

Il est important de prendre l'angle de la plaque et non l'angle défini dans le calque controle, car l'angle de la plaque n'est pas le même puisque nous avons rajouter la valeur "hasard".

La suite, vous connaissez, on a déjà fait ça 100 fois:

X=Math.cos(-angle)*taille;
Z=Math.sin(-angle)*taille;
[X,0,Z]+depart

Et voilà, c'est bon pour la position.
Vous pouvez tester, ça marche. La plaque suit bien la 1ere plaque dans sa rotation.

Passons à présent à la rotation sur l'axe Y. C'est quasiment la même chose que pour la 1ere plaque:

angle=thisComp.layer(thisLayer,-1).rotationY*-1; H=thisComp.layer("controle").effect("random")("Curseur");
seedRandom(index,true);
hasard=random(-H,H);
angle+hasard

Où sont les différences ?
Remarquez le *-1 à la 1ere ligne. On donne à la valeur "angle" l'inverse de la plaque du dessus. Si la plaque 1 est à 20°, cette plaque sera à -20° (+la valeur du hasard)
J'ai donc enlevé la variable "inverse" puisque la 1ere plaque la contient, et que chaque plaque va successivement prendre la valeur inverse à la précédente.

Et voilà !
Vous pouvez dupliquer cette plaque autant que vous voulez et animer la valeur "angle" du calque controle, ça fonctionne.
Vous pouvez créer un Null 3d que vous placez sur le même point que la 1ere plaque, et la mettre en parent de toutes les plaques. Vous pourrez ainsi orienter votre accordéon comme bon vous semble.

Dans le prochain article, nous allons reprendre tout ça à zero, et faire en sorte que l'on puisse définir un retard dans le dépliage, d'un coté ou de l'autre:













Un peu plus compliqué...

mercredi 12 mai 2010

bientôt

Je reviens très bientôt avec 3 nouveaux articles conséquents !

jeudi 6 mai 2010

le cycle de la vie

J'aimerais bien savoir pourquoi il y a un pique de visites tous les mercredis ?
Il y a un centre aéré qui fait un atelier "AE winn3rs, pate-à-sel loos3rs ?"

mardi 27 avril 2010

modèle de début et de fin.

Bonjour à tous, ça faisait longtemps (beaucoup de boulot, et pas de cas d'expression qui vaille le coup)
J'ai eu un commentaire me demandant ceci :
"Comment faire pour caler cette animation de disparition en fonction de la fin du calque (sachant qu'ils n'ont bien sûr pas tous la même durée...)"

Alors oui, ça parait beau comme ça, et vous pensez bien que je m'étais posé la question. Mais malheureusement, c'est plus compliqué que prévu, voilà pourquoi :

Nous avons vu ici comment faire pour qu'une animation se lance quand le début d'un calque se fait sentir. C'était simple, il suffit d'utiliser un valueAtTime pour décaler le temps.

Mais si on veut gérer le début ET la fin, se pose de gros problèmes de logique.
Tout d'abord, cela veut dire qu'il faut définir qui sont les clefs de début, et qui sont les clefs de fin.
C'est une notion très arbitraire, et en rien after effect ne peut le deviner.

L'autre soucis, c'est que pour le début, il suffit de faire un décalage. Là, il faut faire ce décalage, puis arreter la lecture des clefs, puis reprendre la lecture à un moment donné (quand la fin se fait proche).

Ca complique beaucoup les choses, et c'est à se demander si l'energie de la reflexion est rentable comparer à une technique plus manuelle.

Pour un travail, j'ai du trouver une solution, car le fichier after effect était voué à être utiliser par quelqu'un qui ne connait pas du tout after effect, et donc les longueurs de composition allait être variable.
J'ai donc du chercher un moyen de lancer une animation à un moment donné, puis l'arreter, plus la relancer pour l'animation de fermeture.

Je ne donnerai aucune explication, je balance juste l'expression que j'ai utilisé dans un remappage temporel (c'est de toute façon le même principe que pour le remappage de clef).

Je n'ai pas fait le ménage, donc il y a plein de lignes ou de chiffres qui correspondent à ma situation à moi, et ne rentre en aucune façon dans la theorie.

Ce système très très cuisiné vaux le coup quand on a vraiment beaucoup de chose à automatiser...
Donc voilà, pour les plus courageux !

one=comp("prog 1_rush_vendredi").marker.key("fin").time
+
comp("prog 2_rush_vendredi").marker.key("fin").time
+
comp("prog 3_rush_vendredi").marker.key("fin").time
+
comp("prog 4_rush_vendredi").marker.key("fin").time
+
thisComp.layer("prog 1_vendredi").inPoint;


two=comp("source").marker.key("fin").time+one;

ouverture=5;
fermeture=10;
tps=time-inPoint;
x=time;

if (time>ouverture && time
if (time>=one && time
if ( time>one+2 ) { x=fermeture };
if (time>=two) { x=time-two+fermeture};

x


tout le début correspond au fait que ce calque doit démarrer apres 5 calques qui eux mêmes ont exactement la meme chose, c'est à dire un début et une fin totalement aléatoire !
Je vous avais prévenu.