Aide pour After Effect, techniques, expressions.

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.

lundi 15 mars 2010

random VS wiggle

J'ai eu un commentaire me demandant comment avoir un random moins violent.
J'en profite donc pour bien expliquer la différence.

Random permet d'avoir une valeur aléatoire comprise entre x et y, et ce à chaque image. La valeur prise à l'image 5 n'est ni fonction de la valeur à l'image 4, ni à la 6. C'est en ça qu'il se différencie de wiggle.
Le wiggle peut être aussi chaotique que désiré (le chanceux), il possède ce qu'on appelle une corrélation.
Prenons une image :
Prenez une feuille, et dessinez des points, où chacun a sa propre abscisse (x), et une valeur en ordonnée aléatoire (y). Vous obtenez le résultat d'un random.
Reliez tous ces points par une ligne passant par chaque point (sans revenir en arrière), et vous obtenez un wiggle.
Aussi grande que puisse être la différence entre l'ordonnée de 2 points, la valeur wiggle passera nécessairement par toutes les valeurs intermédiaires.
Le wiggle a une corrélation de 100%.
Le random à une corrélation de 0%.

Et ça n'est pas parce que vous avez une composition à 25 images/s, et que vous avez écrit wiggle(25,a) qu'il n'y a pas de lien entre les valeurs. Certes, il prendra bien une nouvelle valeur (une nouvelle crête) à chaque image, mais le lien existe bien. Entre les images, le wiggle prend bien les valeurs intermédiaires.
Il y a plusieurs façon d'en avoir la preuve :
-le motion blur
-un remappage temporel grâce à la technique que nous avons vu ici. En effet, si vous appelez la valeur d'une variable au temps 5.36s, vous aurez bien une valeur intermédiaire.

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é.

dimanche 31 janvier 2010

lampe anisotropique et son flare




Derrière cette recette délicieuse se cache simplement le même principe que la luminosité de Lux, mais appliqué à un flare.
Quelle est la différence à observer ?
Si la lumière volumétrique est bel et bien un phénomène lumineux qui varie selon l'angle avec la caméra, il reste en principe visible dans toutes les directions, même si la lampe est dos à la caméra.

Pour les flares, ça n'est pas le cas. Ils ne sont visibles que lorsque la lampe est en face de la caméra. C'est même dans la nature profonde des flares d'avoir des changements importants au moindre changement d'angle de la lumière.

On peut donc considérer qu'un flare ne commence à apparaître que lorsque la lampe est AU MOINS de profile par rapport à la caméra.

Souvenez vous du petit diagramme avec l'angle de la lampe qui tourne.
La valeur de profil correspond donc à la situation ou les 2 segments sont perpendiculaires.
Les segments valant chacun 1, le vecteur bleu mesure donc racine carré de 2, donc environ 1.4

Il nous suffit donc de modifier la dernière ligne pour que la valeur ne commence non pas à 0, mais à ...hmm ?
Alors alors, souvenez vous, le vecteur bleu va de 2 à 0.
Et on avait transformer ça en valeur qui va de 0 à 1.
C'était donc 1-x/2 pour arriver à ça.

1-1.4/2=0.3

Donc la dernière ligne est :

ease(courbe,0.3,1,min,max)

Et voilà. Votre flare n'apparaîtra qu'à partir du moment où la lampe est dirigée vers la caméra.
Et vous pouvez toujours jouer avec la variable "falloff" pour contraster son apparition

mardi 26 janvier 2010

remappage temporel de clef 2

Certains l'auront peut-être compris, cette technique de remappage de clefs peut également servir à la gestion de plusieurs valeur à la fois.
Par exemple, si vous avez une animation sur une position et sur une rotation, et que vous voulez changer la vitesse d'animation tout en gardant le même rapport entre la position et la rotation.
Imaginez par exemple un homme qui saute d'un plongeoir en faisant des figures. La rotation de son corps sera à priori toujours la même à une position donnée. Autre façon de le dire, la rotation est fonction de la position.

En gros, c'est exactement la même chose, sauf que bien entendu, on va créer la variable dans un calque à part, puisqu'il peut être destiné à plusieurs calques.
On appliquera donc l'expression

target=thisComp.layer("controle vitesse").effect("temps")("Curseur");
valueAtTime(target)

à toutes les valeurs dont on veut contrôler le déroulement.
Mais si vous vous souvenez de l'autre post (ou si vous relisez), nous avions relié la valeur du curseur (celui qui va de 0 à 100) à la valeur à modifier.
Vous pouvez toujours faire ça, mais à quelques conditions:
-la variable qui gère le curseur (la position dans le post précédent) doit avoir sa 1ère clef avant les 1eres clefs de toutes les autres variables (ou au même moment) et sa dernière clef doit être après les dernières clefs des autres variables.
C'est logique, puisque la valeur ne va varier le temps qu'entre les clefs de cette variable.

Ceux qui commence à avoir un peu de bouteille auront sans doute l'intuition qu'il y a d'autres façon de générer cette valeur de temps remappé.
En effet, on peut le voir de plein de façons différentes.
En voici une autre, que vous allez surement préférer dans le cas où il faut gérer plusieurs variables en même temps :

T=thisComp.duration;
linear(value,0,100,0,T)

Voilà, c'est vraiment tout bête.
T vaut la longueur de la composition (en seconde). Quand le curseur vaut 0, le temps vaut 0, et quand le curseur vaut 100, ça renvoie au code temporel de la dernière image de la compo.
Cette expression est plus valable dans le cas où on gère un ensemble de calques/variables, car il n'y a pas de raison pour qu'une variable rentre dans l'équation qui gère un ensemble de variables qui sont au même niveau hiérarchique que cette dernière.

Voilà, il vous suffit donc de bien copier l'expression dans toutes les variables animées que vous voulez.
Vous pourrez ainsi gérer l'ensemble de vos clefs d'une même composition !

Si c'est pas chouette quand même....

recherche

N'hésitez pas à utiliser le champ de recherche pour trouver un article qui traite d'un sujet particulier ou d'une fonction.

jeudi 21 janvier 2010

remappage temporel de clef
















Bonsoir à tous.

Ce soir, un petit outil très pratique qui va vous permettre de gérer la vitesse d'animation d'une valeur sans toucher à cette dernière.
Petit exemple:
Comme vous le savez, quand un objet va d'un point A à un point B, vous pouvez gérer la courbe de vitesse pour faire en sorte que l'objet accélère tout du long, ou freine juste à la fin, etc.

Mais c'est un peu plus compliqué quand l'objet décrit une trajectoire faite de 5 ou 256 points. On peut toujours mettre les points en mode "déplacement dans le temps", mais on ne gère que la vitesse globale et non local (à chaque instant).

Nous allons donc installer un système de remappage temporel, mais pour les clefs.
Vous allez voir, c'est assez simple.

J'explique d'abord ce que l'on va faire:
On va créer une valeur qui va de 0 à 100. Quan la valeur vaudra 0, on chargera la position qui est sur la 1er clef. Quand la valeur vaudra 100, on chargera la valeur qui est sur la dernière clef.

Créez votre objet, faites lui faire une petite trajectoire faite avec 4-5 clefs, qu'importe.
Ensuite, rajoutez lui un paramètre glissière et écrivez ceci :

n=position.numKeys;

On demande que n valle le nombre de clefs dans la valeur "position".

start=position.key(1).time;
end=position.key(n).time;

On crée 2 variables qui nous indiquent à quel code temporel se trouve la 1ere clef et la dernière clef (et oui, la clef "n" sera toujours la dernière. Vous pouvez rajouter des clefs, ça se mettra à jour tout seul).

linear(value,0,100,start,end);

Et voilà !
Il ne vous reste plus qu'à animer ce paramètre glissière en l'animant de 0 à 100.
Cela vous permet de gérer l'accélération/décélération de l'objet, ou vous pouvez aussi faire varier la valeur dans tous les sens pour faire une yoyo, ou même utiliser un wiggle pour aller de 0 à 100.

















A présent, écrivez ceci dans la position de l'objet :

p=effect("Paramètre glissière")("Curseur");
valueAtTime(p)

La position va donc charger la position à un moment différent, celle qui est dans le paramètre glissière.

Ça fonctionne évidemment pour toutes les propriétés.

lundi 18 janvier 2010

ombre et shine

Bonjour à tous.

Tout d'abord, sachez que ce post est la suite du précédent, donc si ce n'est déjà fait, lisez le, car il y a ici plein de choses que je ne réexplique pas.

Suite au post précédent, il est assez logique de voir comment continuer dans la même direction, à savoir que des éléments lumineux soient fonction d'angles.
Nous avons vu comment faire en sorte que la puissance volumétrique d'une lampe soit fonction de son angle par rapport à la caméra.

Voyons à présent le même principe appliqué aux ombres volumétrique.
Vous vous souvenez parfaitement du toComp et ses copains, que nous avions vu ici.
Nous y avions vu comment transformer un point 3d en un point 2, pour l'utiliser par exemple sur un effet.
Dans la 2ème vidéo, on voyait clairement que le "yes we can" avait un shine noir dont la source suivait les coordonnées 3d de la lampe.

Nous allons faire exactement la même chose, mais ce coup-ci, nous allons faire varier la puissance du shine selon l'angle de la lumière avec l'objet.
Et oui, c'est logique, le shine noir (l'ombre) donc, n'apparaît que quand la lumière éclaire l'objet.
De manière générale, il n'y a pas d'ombre sans lumière; un peu de philosophie ne fait jamais de mal.

On comprend donc qu'on va avoir le même type de travail :
Plus la lampe pointe l'objet, plus le shine noir est fort.














J'ai volontairement ici laissé la lampe sans l'effet que nous avons vu précédemment, pour isoler le problème du jour.
Comme vous le voyez, ça fonctionne, avec quelques limites dont je parlerai à la fin.

Voyons ce petit diagramme, qui ressemble un peu au précédent :












L'objet est en haut, la lumière tourne, et on comprend ce qui se passe :
Le shine noir apparaît bien uniquement quand le cône de la lumière passe sur l'objet. Tout ça va donc être fonction du cône de la lumière et de son angle.
J'ai volontairement placé un cercle trigo, car ça va nous servir.

Créez votre petite scène, vous ètes des grands maintenant.
Pour ce qui est du shine, il y a celui de Trapcode donc, mais d'autres éditeurs propose le même effet.
En ce qui concerne celui de Trapcode, il faudra donc mettre "one color" avec du noir ou une couleur foncée, mettre le tout en mode "Multiply".
Créons donc une expression pour "Shine opacity".

P=position;
L=thisComp.layer("Lumière 1");
la=L.position;
lb=L.pointOfInterest;
lv=sub(lb,la);

C'est la même chose que dans le post précédent, la seule différence est qu'on a remplacé la caméra par l'objet.
L'autre différence, c'est que dans l'autre post, on réglait la diminution de la puissance en jouant sur la courbe qui va de 0 à 1.
Ici, on va utiliser l'angle du cone de la lumière.

cone=Math.PI/2-degreesToRadians ( thisComp.layer("Lumière 1").lightOption.coneAngle)/2;

Hou la vilaine ligne !!
Alors, c'est quoi ce truc. C'est assez simple, je vous explique comment ça va se passer.
Nous allons garder nos vecteurs qui nous permettent d'avoir une valeur qui va de 0 à 1.
Ensuite, nous allons transformer cette valeur pour qu'elle aille de 0 à PI/2 (c'est à dire le "haut" du cercle). Nous somme obligéé d'introduire la notion d'angle puisque nous allons devoir en introduire un.
Nous allons donc uniquement travailler avec le quart supérieur droit du cercle trigo, qui va donc bien de 0 à PI/2, PI/2 correspondant à une lumière qui point exactement l'objet.
Sauf que ce coup-ci, quand l'angle vaut 0 (et donc que la lumière est parfaitement dos à l'objet), la valeur vaut bien 0, mais doit y rester pour tous les angles environnant.
Et oui, les seuls valeurs non nulles ne doivent apparaître que lorsque la valeur approche PI/2, et plus exactement Pi/2-angleDuCone.













Regardez le schéma, et notamment les angles arcs rose et vert (qui sont égaux)
Le vert représente l'angle du cône de la lumière. Le rose représente l'angle de la lumière avec l'objet.
On comprend bien que quand l'angle lumière-objet vaut PI/2-angleCone, la valeur du shine noir va commencer à augmenter.
Continuons :

P_la=normalize ( sub(la,P) );
delta=add(P_la,lvn);
val=1-length(delta)/2;

Même chose que dans le post précédent donc, à l'exception de la caméra remplacer par l'objet.
val varie bien de 0 à 1, faisons le varier de 0 à PI/2.

angle=linear(val,0,1,0,Math.PI/2);

Voilà qui est fait.
A présent, la dernière ligne :

ease(angle,cone,Math.PI/2,0,100);

cone est bien notre valeur minimum, qui correspond donc bien à 0, et Math.PI/2 à la valeur maximum.
L'utilisation du ease plutôt que du linear est subjective elle permet juste de lisser un peu les transition.

Vous pouvez bien entendu remplacer le 100 par une variable.

A présent, voyons les limites de l'effet.
Comme d'habitude, la seule chose qui est prise en compte est le point d'ancrage de l'objet (donc pour un texte, penser bien à ce qu'il soit centré, et non aligné à gauche ou à droite). Ca ne prend donc pas en compte la largeur ou la hauteur du calque, et l'effet sera appliqué à l'ensemble du calque, même s'il n'y a que la moitié du calque qui est dans la lumière.

A cause de cela, rajoutons un paramètre glissière au calque appelé "correction", et écrivons ceci :

P=position;
L=thisComp.layer("Lumière 1");
la=L.position;
lb=L.pointOfInterest;
lv=sub(lb,la);
lvn=normalize(lv);
cone=Math.PI/2-degreesToRadians ( thisComp.layer("Lumière 1").lightOption.coneAngle);
correction=degreesToRadians ( effect("correction")("Curseur"));

P_la=normalize ( sub(la,P) );
delta=add(P_la,lvn);
val=1-length(delta)/2;

angle=linear(val,0,1,0,Math.PI/2);
ease(angle,cone-correction,Math.PI/2,0,100);

Voilà, comme ça , vous pourrez virtuellement augmenter ou diminuer l'angle du cône, pour que la puissance du shine noir se maintienne sur un angle plus ou moins large par rapport à l'angle réel.

vendredi 15 janvier 2010

éclairage volumétrique anisotropique

Bonjour à tous,

ça fait un bail, je vous l'accorde, mais comme je n'avais pas écrit de nouvelles expressions, je n'allais pas venir et raconter rien du tout.

Quand j'ai un problème technique dans After Effect, je cherche une solution, et quand la solution contient des expressions, ça vient logiquement s'échouer ici.
C'est le cas aujourd'hui, j'ai essayé de corriger une lacune du plugin Lux de Trapcode.

Ce plugin relavement connu est très pratique, puisqu'il vous permet de simuler un éclairage volumétrique, à savoir que la lumière soit filtrée par les particules dans l'air.

Vous trouverez tous les renseignements ici :
Trapcode Lux

Peut être existe-il d'autres éditeurs qui proposent un effet équivalent, mais je n'en connais pas d'autre.
En général, j'essaye d'éviter de parler de plugin, car ça ne concerne pas tout le monde, mais celui ci est assez connu.
Venons en au fait :
Le "défaut" de Lux (qui sera je l'espère corrigé dans un théorique 2.0 ?....) est que la puissance n'est que fonction de la puissance de la lampe, et non de l'angle de la lampe avec la caméra.

avec Lux :













avec Lux+saucisse:












Et oui, quand vous êtes dans une ambiance poussiéreuse, le faisceau filtré par les particules est beaucoup plus puissant (pour vous) quand la lumière est dans votre direction.

La raison en est simple, il faut considérer la poussière comme un volume à part entière, comme de la cire par exemple. La cire, ça n'est pas vraiment transparent, mais la lumière passe quand même à l'intérieur. Elle se diffuse même.
C'est cette diffusion qui nous intéresse, car elle n'est pas égale dans toutes les directions.
Envoyez un faisceau laser vers de la cire, la lumière va se diffuser, mais avec une nette préférence pour la direction initiale de la lumière.

C'est la même chose avec la poussière.

C'est ce qu'on appelle une diffusion anisotropique, par opposition à isotropique, qui veut dire "égale dans toutes les directions".

Bref, tout ce blabla pour dire qu'on va faire en sorte que la puissance de la lumière (de Lux) soit fonction de l'angle de la lumière avec la caméra.

Ça va pas être très compliqué, alors allons y.
Voyons ce petit schéma :

















Nous avons donc une caméra (dont la source s'appelle "ca") et une lumière dont nous appelleront la source "la" et la cible "lb")
En gros, la lumière sera au plus fort quand elle sera exactement dans l'axe de la camera, c'est à dire que le vecteur (ca,la) sera dans le meme axe que le vecteur (la,lb), mais dans la direction opposée.

Est-ce aussi simple que ça cela ?
On pourrait en effet créer un modèle beaucoup plus compliqué, en prenant en compte par exemple l'angle du cône de la lumière. En effet, plus le cône est large, plus l'intensité restera forte sur une plage angulaire importante. Cela mériterait en effet d'entrer dans l'équation.
Mais nous allons faire sans (et également sans la focale de la camera), et nous verrons à la fin pourquoi. Ne vous inquiétez pas, il ne s'agit en rien d'une quelconque paresse.

Commencez par créer votre petite scène, avec votre caméra, votre spot, et votre calque Lux.
Ensuite, rajoutez 3 paramètres glissière au même calque Lux :
-falloff
-val min
-val max

Ensuite, revenez dans l'effet Lux, aller dans spot Lights, et créez une expression pour "Intensity"

Commençons par créer nos variables :

falloff=effect("falloff")("Curseur");
min=effect("val min")("Curseur");
max=effect("val max")("Curseur");

Ensuite :

ca=thisComp.layer("Caméra 1").transform.position;
la=thisComp.layer("Lumière 1").transform.position;
lb=thisComp.layer("Lumière 1").transform.pointOfInterest;
lv=sub(lb,la);
lvn=normalize(lv);

Pas grand chose à expliquer à des gens aussi fort que vous, on définit la position de la caméra, la position de la lampe, puis le vecteur qui donne la direction de la lampe.
Pour rappelle, quand on veut créer un vecteur AB, on écrit sub(B,A);
Je ne me souviens plus si on a déjà vu le normalize, alors dans le doute, il permet de transformer un vecteur donné(qui je vous rappelle, a 2 composantes : une direction, et une longueur) en un vecteur de même direction, mais de longueur 1. On le normalise quoi....
Pourquoi fait-on ça ? Car nous allons le comparer au vecteur (camera,lampe). Et comme la distance entre la lampe et sa cible peut prendre n'importe quelle valeur et qu'elle n'intervient pas dans le calcul (seul la direction nous intéresse), on normalise.

Vous suivez ? On continue.
Créons le vecteur (camera,lampe):


ca_la=normalize ( sub(la,ca) );

J'aurais pu l'écrire en 2 lignes (une pour la création du vecteur, l'autre pour la normalisation).
Nous avons à présent 2 vecteurs de taille égale à 1, avec chacun une direction différente (à priori)

On comprend donc que si on fait vecteur1+vecteur2, on va obtenir un 3ème vecteur qui aura une taille donnée. C'est la taille de ce vecteur qui va nous dire à quel point les 2 vecteurs ont des directions différentes.
Imaginez que la lampe soit exactement dos à la caméra. Si on additionne les deux vecteurs, comme ils sont dans la même direction, on a donc un 3ème vecteur d'une taille de 2.
Si la lampe est pile poil dans l'axe de la camera, ça fait 1-1=0.
Si les deux vecteurs sont perpendiculaires, on obtient donc un troisième vecteur égale à racine de 2.




Trouvons le vecteur bleu :

delta=add(ca_la,lvn);

On a bien additionné le vecteur qui va de la caméra à la lumière avec le vecteur allant de la lumière à la cible de la lumière. Chaque vecteur est égal à 1, le vecteur bleu varie bien de 0 à 2.
Attention, sur le petit schéma, ne pensez pas que la ligne blanche correspond à l'axe de la caméra, ça n'est pas du tout le cas. Il s'agit bien du vecteur (caméra,lampe).

Revenons à la base, on est en train de régler l'intensité. Il nous faut donc une variable qui va de 0 à 1.
Transformons donc delta qui va de 2 à 0 pour qu'il aille de 0 à 1:

angle=1-length(delta)/2;

C'est maintenant que la finesse se passe. On a bien notre variable qui va de 0 à 1. Il suffit par exemple de multiplier cette valeur par 100, et on a notre effet.
Sauf qu'il nous manque un petit réglage. En effet, on ne peut pas régler la façon dont on va de 0 à 1. Pour le moment, c'est linéaire.
Par exemple, quand la lumière passe de "face" à "profile",on passe de 1 à 1-1.4 (en gros) de façon linéaire, et même chose pour "profile" à "dos".
Sauf que ça n'est pas ce qu'on veut. Quand la lumière est de face, on est à 100%, et plus la lumière s'écarte, plus la puissance baisse vite. La courbe est donc exponentielle.
Il faut donc qu'on casse cette ligne droite qui va de 0 à 1. Pour parler intuitivement, il faut que la courbe ait du mal à arriver à 1, que 1 soit un pic, un roc ou ce que vous voulez.

C'est pour cela qu'on a créé la variable "falloff".

Voici la ligne suivante :

courbe=Math.pow(angle,falloff);

On va donc avoir notre variable qui va de 0 à 1 à la puissance falloff.
Et comme on est entre 0 et 1, on reste entre 0 et 1. Plus falloff est grand, plus la courbe est prononcée, plus l'angle devra être "parfait" pour atteindre 1.
Vous comprenez maintenant la valeur qui défile dans la vidéo, il s'agit de la puissance de la lampe, vous pouvez ainsi voir que la valeur ne commence à augmenter réellement qu'à proximité de l'axe de la caméra.

Voilà pourquoi je n'ai pas créé de variable pour le cone de la lampe ou autre.
Tout se fait avec la variable falloff qui vous permettra de régler votre effet. Plus vous voulez une lumière directionnelle, plus il faudra augmenter la valeur. En gros, vos valeurs iront de 5 à 100, pour vous donnez un ordre de grandeur.

Il ne manque plus que la dernière ligne :

ease(courbe,0,1,min,max)

Voilà, il ne vous reste plus qu'à mettre des valeurs dans val min et val max (comme 10 et 100 par exemple), et le tour est joué.

Voilà, c'était long car je voulais bien expliquer la petite réflexion qui permet d'arriver à ça, car en suite, l'expression est simplissime, on a fait 10 fois pire ensemble.
Si on enlève les lignes de création de variable, il n'y a que 5-6 lignes.


Encore une petite précision tout de même. Vous risquez d'avoir un problème si votre lampe a un parent. En effet, After Effect va modifier les valeurs de position et de point cible en fonction du parent. Le problème vient du fait que pour les expressions, AE récupère les valeurs de chaque variable sans prendre en compte les parents. Vous obtiendrez donc des valeurs erronées.
Je parle de ça car il est assez commun qu'une lampe ait un parent, pour par exemple pouvoir déplacer les deux points de la lampe en même temps.

La solution est de définir la position ainsi :

position=objet.position+parent.position