最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

【AE表達(dá)式】createPath創(chuàng)建可控制彈性繩 //用于MG彈跳動(dòng)畫

2021-08-22 22:34 作者:Cubx  | 我要投稿


/*表達(dá)式文末自取*/

關(guān)于彈性運(yùn)動(dòng)網(wǎng)上都有表達(dá)式可以直接拿來用的

本文就不講如何用表達(dá)式實(shí)現(xiàn)彈性運(yùn)動(dòng)了/*主要是我也不會(huì),也許以后會(huì)*/

//其實(shí)文章標(biāo)題應(yīng)該叫做 表達(dá)式控制正弦函數(shù)圖像的各種變化

分析

小球的彈性運(yùn)動(dòng)直接上表達(dá)式:/*反正我也是抄的*/

a=2;//振蕩頻率

b=2;//衰減率

n=0;

if (numKeys>0){

?????? n=nearestKey(time).index;

?????? if (key(n).time>time) n--;}

if (n>0){

?????? t=time-key(n).time;

?????? amp=velocityAtTime(key(n).time-.001);

?????? w=a*Math.PI*2;

?????? value+amp*(Math.sin(t*w)/Math.exp(b*t)/w);}

else{value}

//添加關(guān)鍵幀動(dòng)畫后才有效果

繩子部分使用createPath創(chuàng)建正弦函數(shù)路徑,并使繩子末端跟隨小球運(yùn)動(dòng),頂端不動(dòng)

/*如果你不會(huì)用createPath,我這有個(gè)好康的cv6759435

后面我也會(huì)提一下,就只提一下,不能提多了,畢竟文章中心是控制正弦曲線

總之這個(gè)表達(dá)式非常好玩*/

目標(biāo)

1、 可改變彈性繩的圈數(shù)和寬度/*頻率和振幅*/

2、 通過移動(dòng)兩個(gè)控制點(diǎn)操控彈性繩位置與水平長(zhǎng)度

同時(shí)繩子自身長(zhǎng)度不變//近似不變

3、 曲線折線的切換

4、 彈性繩的相位變化

思路

控制振幅和頻率:建立y=lon*sin(fx) 的函數(shù),lon:振幅,f:頻率

控制位置:

為函數(shù)圖像上所有點(diǎn)加上首點(diǎn)/*第一控制點(diǎn)*/的坐標(biāo)

讀取兩控制點(diǎn)的間距控制圖像水平距離//以便圖像上最后一個(gè)點(diǎn)與末點(diǎn)/*第二控制點(diǎn)*/重合

通過兩點(diǎn)坐標(biāo)差的商求出圖像整體需要旋轉(zhuǎn)的角度

//順手做個(gè)動(dòng)畫以便理解

保持繩長(zhǎng)固定:

通過弧微分公式//你確實(shí)可以這樣做,但之后你會(huì)為了求個(gè)弧長(zhǎng)自學(xué)橢圓積分、階乘Γ函數(shù)、高斯常數(shù)…先不說浪費(fèi)時(shí)間,做個(gè)動(dòng)畫也不至于這么卷

曲線長(zhǎng)難求,可以用直線代替

讓x只取 π/2 倍數(shù),就可以得到這些位置的點(diǎn)

//createPath的畫圖邏輯就是描點(diǎn)連線,所以目前只是折線

再用勾股定理表示一段直線的平方:lon^2+(T/4)^2 確保該式為定值//T為圖像周期水平長(zhǎng)度

曲線/折線轉(zhuǎn)換:無疑就是是否有切線的區(qū)別,使用if判斷語句

相位變化:自變量x后面加參數(shù)c以控制相位

實(shí)現(xiàn)

在一切的開始,請(qǐng)務(wù)必用表達(dá)式把圖層的PSR屬性鎖好,因?yàn)槭髽?biāo)很容易誤操作改變圖層形態(tài),導(dǎo)致函數(shù)圖像位置錯(cuò)誤

//所有表達(dá)式寫在一個(gè)形狀圖層中,這樣可以方便復(fù)制到別的合成里

初始設(shè)置

/* createPath(a,b1,b2,c)共四個(gè)參數(shù),使用于路徑中

a為二維數(shù)組(必要),b1出點(diǎn)數(shù)組,b2入點(diǎn)數(shù)組,c為true/false表示是否閉合路徑

a = [ x , y , z …] x y z等 是點(diǎn)的位置/*位置是含有兩個(gè)元素的數(shù)組*/,從左向右讀取連線 */

創(chuàng)建形狀圖層,效果面板里添加并重命名一些表達(dá)式控制//也可以先把名字命名為參數(shù)

鋼筆工具在視圖窗中畫個(gè)點(diǎn),內(nèi)容中就會(huì)出現(xiàn)形狀 1

然后按住Alt點(diǎn)擊路徑前面的圈,打開表達(dá)式編輯框

先定義幾個(gè)變量:

f=effect("頻率")("滑塊");//最重要,不能<=0

p=effect("點(diǎn)密度")("滑塊");//表示單位長(zhǎng)度內(nèi)點(diǎn)的數(shù)量有點(diǎn)雞肋

a=effect("寬度")("滑塊");//用于控制振幅lon

c=effect("相位")("滑塊");

x1=effect("首點(diǎn)")("點(diǎn)")[0];y1=effect("首點(diǎn)")("點(diǎn)")[1];//用于控制位置和方向

x2=effect("末點(diǎn)")("點(diǎn)")[0];y2=effect("末點(diǎn)")("點(diǎn)")[1];

pots=[];ins=[];outs=[];//要傳給createPath的二維數(shù)組

lon=100;hoz=50;m=1;n=10;//先定義幾個(gè)待會(huì)要用的參數(shù)

建立函數(shù)

創(chuàng)建一個(gè)正弦函數(shù):

function fx(t){

?????? x = t;

?????? y = lon * Math.sin( f * t );//函數(shù)lon*sinfx

?????? return [ x , y ]+[ x1 , y1 ];//所有點(diǎn)受首點(diǎn)控制

}

使用時(shí)只需要輸 fx/*方法名,隨便取*/(0),就返回x=0時(shí)點(diǎn)的位置

但一個(gè)一個(gè)輸x值太麻煩,就加一個(gè)for循環(huán)自動(dòng)取點(diǎn):

for (i=0;i<=n;i=i+1){//n表示點(diǎn)數(shù),之后由別的參數(shù)進(jìn)行控制

?????? w = Math.PI/2/f; //只取x = π/2 倍數(shù)的點(diǎn),除以f是因?yàn)閳D像上兩點(diǎn)間距與f成反比

?????? pos = fx(i*w);

?????? pots.push(pos); //每次循環(huán)向數(shù)組pots 添加一個(gè)點(diǎn)的位置

}

最后寫:

if (effect("切線")("復(fù)選框")==0){

?????? ins = [] ; outs = [] ;

}//如果復(fù)選框沒勾選的話,就讓ins和outs成為空數(shù)組,就沒有切線

createPath( pots , ins , outs , 0 )

于是你得到了一條只有聰明人才看得見的sinx函數(shù)曲線

曲線水平方向太小,是因?yàn)辄c(diǎn)的x值 = i/f*w ,這三個(gè)參數(shù)都是個(gè)位數(shù)

所以畫出來的圖像上相鄰兩點(diǎn)間距很短//大概就幾個(gè)像素

放大水平長(zhǎng)度

解決方法就是讓函數(shù)輸出的x值乘一個(gè)參數(shù)hoz:

function fx(t){

?????? x = t;

?????? y = lon * Math.sin( f * t );//函數(shù)lon*sinfx

?????? return [hoz * x , y ] +[ x1 , y1 ];//hoz 控制水平長(zhǎng)度

}

位置控制

現(xiàn)在開始進(jìn)入正題

要讓圖像水平長(zhǎng)度跟隨首點(diǎn)和末點(diǎn)的距離變化,就讓這兩個(gè)量進(jìn)行聯(lián)系

讓參數(shù) l 表示兩點(diǎn)間距:

?l=Math.sqrt(Math.pow((x2-x1), 2)+Math.pow((y2-y1), 2));

要使圖像最后一個(gè)點(diǎn)與末點(diǎn)重合,l 就應(yīng)為圖像周期長(zhǎng)度的倍數(shù)

根據(jù)函數(shù)求得周期長(zhǎng)度為 hoz*2π/f

/*代碼函數(shù)本身的周期長(zhǎng)度并不是這個(gè),由于代碼函數(shù)輸出的點(diǎn)為[hoz * x , y ],做出圖像的函數(shù)實(shí)則變了y = lon * sin( f * x / hoz ),該函數(shù)才是真正作圖的函數(shù),知道這個(gè)對(duì)寫導(dǎo)函數(shù)有大用*/

我們還希望頻率f 為1時(shí),首末點(diǎn)間只有一個(gè)周期長(zhǎng)度圖像,為2時(shí)有兩個(gè)

則可以讓l = hoz*2π,只需要在function前面輸:

hoz=l/2/Math.PI;

hoz解決了,但還有點(diǎn)數(shù)n的問題,沒有足夠的n就不能生成足夠長(zhǎng)的線

n應(yīng)與l和f成正比,但n與f的關(guān)系更明顯

f為1時(shí),首末點(diǎn)間只有一個(gè)圖像周期的所有點(diǎn),點(diǎn)數(shù)為5,n=4

//因?yàn)樵趂or循環(huán)取值時(shí)i取0、1、2、3、4剛好5個(gè)點(diǎn)

f為2時(shí),首末點(diǎn)間有兩個(gè)圖像周期的所有點(diǎn),點(diǎn)數(shù)為5+4=9,n=8

很容易看出關(guān)系,把之前的n改為:

n = 4 * f ;

方向控制

但現(xiàn)在還不能讓圖像跟著首末點(diǎn)跑,它還不能旋轉(zhuǎn)

進(jìn)入內(nèi)容-形狀 1-變換-旋轉(zhuǎn)表達(dá)式編輯界面

/*選中圖層按快捷鍵R的是圖層旋轉(zhuǎn)屬性,它應(yīng)該被表達(dá)式鎖住的

這個(gè)是形狀 1的旋轉(zhuǎn)屬性*/

直接輸:

x1=effect("首點(diǎn)")("點(diǎn)")[0];y1=effect("首點(diǎn)")("點(diǎn)")[1];

x2=effect("末點(diǎn)")("點(diǎn)")[0];y2=effect("末點(diǎn)")("點(diǎn)")[1];

dy=y2-y1;dx=x2-x1;//xy坐標(biāo)差求角度

radiansToDegrees(Math.atan2(dy, dx))

//原理上文的動(dòng)圖解釋得很直觀了

錨點(diǎn)跟隨首點(diǎn)

但移動(dòng)控制點(diǎn)時(shí)就發(fā)現(xiàn)圖像并不以控制點(diǎn)為中心旋轉(zhuǎn)

/*這不廢話?圖像肯定繞錨點(diǎn)旋轉(zhuǎn)啊*/

改變錨點(diǎn)的參數(shù)只會(huì)移動(dòng)圖像,改變位置的參數(shù)則錨點(diǎn)和圖像一起動(dòng)

于是在錨點(diǎn)和位置屬性里同時(shí)輸入:

x1=effect("首點(diǎn)")("點(diǎn)")[0];y1=effect("首點(diǎn)")("點(diǎn)")[1];

[x1,y1]//首點(diǎn)控制路徑

讓錨點(diǎn)跟隨首點(diǎn)的同時(shí)不改變位置,現(xiàn)在圖像可以被完全操控了

//如果還不行就重新檢查圖層的位置和錨點(diǎn)屬性是否都為[ 0 , 0 ]

繩長(zhǎng)固定

根據(jù)思路用直線代替弧長(zhǎng)

讓圖像中一段直線長(zhǎng)度的平方:lon^2+(T/4)^2成為定值

于是假設(shè)這個(gè)定值為a*k/*k為常數(shù)*/圖像周期長(zhǎng)度T/4為l/(4*f)

則lon^2+( l/(4*f))^2=(a*k)^2 ,于是輸入:

lon=-Math.sqrt(a*100-Math.pow(l/4/f, 2));

//經(jīng)過調(diào)參決定把k設(shè)為100,

//AE中Y軸正方向是向下的,所以lon改為負(fù)值

生成切線

目前為止我們只畫了個(gè)折線,形成曲線得添加切線

切線就是滑桿或手柄//會(huì)用鋼筆工具的你一定知道手柄是什么

手柄有出點(diǎn)和出點(diǎn),這里只需要確定出點(diǎn)的位置然后傳給數(shù)組ins

//“出點(diǎn)的位置”指出點(diǎn)相對(duì)與父點(diǎn)的位置,并非其世界坐標(biāo)

父點(diǎn)就是在圖像上的點(diǎn),我們要基于這些點(diǎn)確定出點(diǎn)的位置

要確定出點(diǎn)位置就應(yīng)先知道切線斜率

切線斜率用導(dǎo)數(shù)公式求,函數(shù)代碼下方輸入導(dǎo)函數(shù):

function dy(t){

?????? k=lon*f*Math.cos(f*t)/hoz;//求斜率

?????? x=-Math.cos(Math.atan(k)); //因?yàn)槭浅鳇c(diǎn),所以為負(fù)

?????? y=-Math.sin(Math.atan(k));

?????? return [x,y]; //輸出出點(diǎn)的位置

}

/*根據(jù)作圖的函數(shù)y=lon*sin(f*x/hoz)求出導(dǎo)函數(shù)lon*f*cosfx /hoz

注意作圖函數(shù)自變量為x/hoz,而導(dǎo)函數(shù)自變量為x,不理解就別理解吧*/

同理,用for循環(huán)把所有出點(diǎn)位置傳給數(shù)組ins:

for (i=0;i<=n;i=i+1){

?????? w=Math.PI/2/f;

?????? pos=fx(i*w);inp=dy(i*w);//給函數(shù)和導(dǎo)函數(shù)的x值一樣

?????? pots.push(pos);ins.push(inp);outs.push(-inp);//入點(diǎn)位置就是出點(diǎn)位置的反方向

}

現(xiàn)在打開效果控件里的切線開關(guān),就可以得到聰明人也看不見的切線了

切線長(zhǎng)度

還是同理,切線長(zhǎng)度只有一個(gè)像素左右大,于是改導(dǎo)函數(shù):

function dy(t){

?????? k=lon*f*Math.cos(f*t)/hoz;//求斜率

?????? x=-Math.cos(Math.atan(k));

?????? y=-Math.sin(Math.atan(k));

?????? return [m*x,m*y]; //m控制切線長(zhǎng)度

}

m應(yīng)隨著l的增長(zhǎng)而增長(zhǎng),隨著f的增加而減短

所以輸入:

m = o * l / f;

//o為常數(shù),建議為參數(shù)o創(chuàng)建一個(gè)滑塊控制,調(diào)節(jié)圖像與由描點(diǎn)連線形成的圖像進(jìn)行契合,找出最佳契合常數(shù),大概是0.1左右吧

你可能會(huì)覺得m應(yīng)該也與振幅控制系數(shù)a有關(guān),但如果不管a的影響的話,契合誤差還是比較小的

/*圖中灰色為由切線作出的圖像,淺綠色是描點(diǎn)連線的圖像

貝塞爾曲線不可能完全契合sinx函數(shù)圖像*/

相位

注意函數(shù)和導(dǎo)函數(shù)都要加相位控制參數(shù)c:

function fx(t){

?????? x=t;

?????? y=lon*Math.sin(f*t+c/*相位*/);

?????? return [hoz*x+x1,y+y1]

}

function dy(t){

?????? k=lon*f*Math.cos(f*t+c/*相位*/)/hoz;

?????? x=-Math.cos(Math.atan(k));

?????? y=-Math.sin(Math.atan(k));

?????? return [m*x,m*y];

}

建立點(diǎn)密度/*可略過*/

點(diǎn)密度p就是單位長(zhǎng)度內(nèi)點(diǎn)的數(shù)量,在一些極端情況/*比如振幅極大時(shí)*/可以通過增加點(diǎn)密度讓曲線生成的圖像更像sinx函數(shù)圖像

p=1????對(duì)比????p=2

原理就是控制圖像上相鄰兩點(diǎn)水平間距:

for (i=0;i<=n;i=i+1){

?????? w=Math.PI/2/f/p;//使p增大時(shí)兩點(diǎn)間距減小

?????? pos=fx(i*w);inp=dy(i*w);

?????? pots.push(pos);ins.push(inp);outs.push(-inp);

}

圖像上兩點(diǎn)間距減小,那點(diǎn)數(shù)n也就要增加:

n=f*p*4;

點(diǎn)數(shù)n多了,切線長(zhǎng)度m就要減小:

m=o*l/f/p;//o為由你調(diào)出的常數(shù)

/*點(diǎn)密度p可能有點(diǎn)雞肋,但還是建議加上它,說不定就用上了*/

參數(shù)控制

做到這里基本結(jié)束了,接下來優(yōu)化代碼,處理一些報(bào)錯(cuò)的情況

心血來潮把點(diǎn)密度p調(diào)得過大導(dǎo)致點(diǎn)數(shù)過多,使循環(huán)次數(shù)過多導(dǎo)致電腦爆炸

同理f也是,使用clamp限制其數(shù)值:

p=clamp(p,20,0.1);

f=Math.round(clamp(f,20,1));

//f取整使首末點(diǎn)始終位于圖像中間,f必須>0

心血來潮讓首末點(diǎn)間距過長(zhǎng)則發(fā)生報(bào)錯(cuò),因?yàn)槭啄c(diǎn)間距過長(zhǎng)時(shí)

振幅公式lon=-Math.sqrt(a*100-Math.pow(l/4/f, 2));

中的a*100-Math.pow(l/4/f, 2)成為負(fù)數(shù),則Math.sqrt()不能計(jì)算

用if判斷語句解決:

if (a*100 < Math.pow(l/4/f, 2)){

lon=0;

}

else{

lon=-Math.sqrt(a*100-Math.pow(l/4/f, 2));

}

使當(dāng)l過長(zhǎng)時(shí)振幅lon為0,形成一條直線

總結(jié)

后來覺得可以改進(jìn)繩長(zhǎng)固定的代碼,于是用微分的方法把弧長(zhǎng)確定到了個(gè)位

但算出了弧長(zhǎng)后不知道怎么根據(jù)弧長(zhǎng)控制振幅lon,就傻了

發(fā)現(xiàn)自己花了一堆時(shí)間整出個(gè)沒用的sin弧長(zhǎng)計(jì)算代碼

//本來連圖都畫出來了的……

前天看了大佬的三篇文章cv6759435,從而了解createPath的使用方法

于是前天晚上就有一個(gè)這樣的想法,當(dāng)天半夜寫出了個(gè)大概,昨天進(jìn)行代碼各種功能的完善,下午加晚上寫文章和做gif動(dòng)圖,今天用一堆時(shí)間整弧長(zhǎng)

/*總之createPath這個(gè)表達(dá)式非常好玩*/

?

感覺這些表達(dá)式可以用在很多mg動(dòng)畫的方面,不僅僅是彈跳動(dòng)畫

之后會(huì)稍微研究一下下面的兩種彈性表達(dá)式

?代碼/*與上文有一點(diǎn)出入*/

地面反彈

e=effect("彈力系數(shù)")("滑塊");

g=10*effect("重力 *10")("滑塊");

nMax=effect("最大反彈次數(shù)")("滑塊");

n=10;

if(numKeys>0){

?????? n=nearestKey(time).index;

?????? if(key(n).time>time)n--;}

if(n>0){

?????? t=time-key(n).time;

?????? v=-velocityAtTime(key(n).time-.001)*e;

?????? vl=length(v);

?????? if(value instanceof Array){

????????????? vu=(vl>0)? normalize(v):[0,0,0];}

?????? else{vu=(v<0)?-1:1;}

tCur=0;

segDur=2*vl/g;

tNext=segDur;

nb=1;//Number of bounces

while(tNext < t && nb <= nMax){

?????? vl*=e;

?????? segDur*=e;

?????? tCur=tNext;

?????? tNext+=segDur;

?????? nb++}

if(nb<=nMax){

?????? delta=t-tCur;

?????? value+vu*delta*(vl-g*delta/2);}

else{value}

}

else{value}

彈性振蕩

a=effect("振蕩頻率")("滑塊");

b=effect("衰減率")("滑塊");

n=0;

if (numKeys>0){

?????? n=nearestKey(time).index;

?????? if (key(n).time>time) n--;}

if (n>0){

?????? t=time-key(n).time;

?????? amp=velocityAtTime(key(n).time-.001);

?????? w=a*Math.PI*2;

?????? value+amp*(Math.sin(t*w)/Math.exp(b*t)/w);}

else{value}

/*兩個(gè)彈性表達(dá)式都是網(wǎng)上找到的其中一種,都需要添加關(guān)鍵幀動(dòng)畫*/

彈性繩路徑

f=effect("頻率")("滑塊");

p=effect("點(diǎn)密度")("滑塊");

a=effect("寬度")("滑塊");c=effect("相位")("滑塊");

x1=effect("首點(diǎn)")("點(diǎn)")[0];y1=effect("首點(diǎn)")("點(diǎn)")[1];

x2=effect("末點(diǎn)")("點(diǎn)")[0];y2=effect("末點(diǎn)")("點(diǎn)")[1];

pots=[];ins=[];outs=[];

?

/*設(shè)置參數(shù)閾值,避免報(bào)錯(cuò)*/

p=clamp(p,20,0.1);f=Math.round(clamp(f,20,1));

?

/*參數(shù)控制*/

n=f*p*4;//點(diǎn)數(shù)自動(dòng)改變

l=Math.sqrt(Math.pow((x2-x1), 2)+Math.pow((y2-y1), 2));//首末點(diǎn)距離

hoz=l/2/Math.PI;//水平距離由l控制

?????? if (a*100 < Math.pow(l/4/f, 2)){lon=0;}//防止因l過長(zhǎng)的報(bào)錯(cuò)

?????? else{lon=-Math.sqrt(a*100-Math.pow(l/4/f, 2));}//振幅控制,使線段總長(zhǎng)一致

m=-0.1001*l/f/p;//切線長(zhǎng)度調(diào)節(jié)

?

/*函數(shù)部分*/

function fx(t){

?????? x=t;

?????? y=lon*Math.sin(f*t+c/*相位*/);//函數(shù)lon*sinfx

?????? return [hoz*x+x1,y+y1]//曲線從首點(diǎn)出發(fā),用hoz放大水平長(zhǎng)度

}

function dy(t){

?????? k=lon*f*Math.cos(f*t+c)/hoz;//導(dǎo)數(shù)lon*f*cosfx,得出切線斜率

?????? x=Math.cos(Math.atan(k));

?????? y=Math.sin(Math.atan(k));//返回入點(diǎn)位置

?????? return [m*x,m*y];//m調(diào)節(jié)切線長(zhǎng)度

}

?

/*通過循環(huán)獲取點(diǎn)*/

for (i=0;i<=n;i=i+1){

?????? w=Math.PI/2/p/f;//兩點(diǎn)間距

?????? pos=fx(i*w);inp=dy(i*w);

?????? pots.push(pos);ins.push(inp);outs.push(-inp);

}

?

/*切線控制*/

if (effect("切線")("復(fù)選框")==0){

?????? ins=[];outs=[];

}

?

/*創(chuàng)建路徑*/

createPath(pots,ins,outs,0)


//關(guān)于形狀屬性的表達(dá)式詳見方向控制

2022.6.15 寫了腳本,粘貼到記事本里保存,后綴改成.jsx就能用了


【AE表達(dá)式】createPath創(chuàng)建可控制彈性繩 //用于MG彈跳動(dòng)畫的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
温泉县| 珠海市| 于都县| 榆中县| 财经| 南平市| 涟源市| 宜阳县| 如东县| 鹤庆县| 开远市| 凤冈县| 陈巴尔虎旗| 怀来县| 丰台区| 乡城县| 景泰县| 成都市| 扎鲁特旗| 白水县| 忻城县| 息烽县| 开阳县| 贵阳市| 寿宁县| 福安市| 大安市| 盐池县| 台江县| 菏泽市| 富川| 西宁市| 吴堡县| 安阳市| 平塘县| 深泽县| 淳化县| 大荔县| 宜黄县| 马尔康县| 澄江县|