[AE表達(dá)式]使用createPath創(chuàng)建與控制曲線_Part.2
如果createPath的points參數(shù)會(huì)用了,基本切線研究一下也就會(huì)了,這篇主要解釋下如何在點(diǎn)與手柄控制器有父子級(jí)的情況下傳遞正確的空間位置。

首先要理解AE中不同空間的坐標(biāo)系,無論在二維還是三維中,坐標(biāo)都有全局坐標(biāo)(世界坐標(biāo))和局部坐標(biāo)(本地坐標(biāo))之分,我們先在空合成中創(chuàng)建一個(gè)空物體,看一下它的坐標(biāo)

AE的坐標(biāo)設(shè)定是從合成最左上角為[0,0], 最右下角是合成大小的長(zhǎng)*,我的合成長(zhǎng)寬是1920*1080的,所以最中心坐標(biāo)是長(zhǎng)寬的一半960*540
要注意的是,由于Y軸0點(diǎn)在上,向下為正,所以AE坐標(biāo)Y軸數(shù)值增加是物體向下移動(dòng)(所以C4D物體導(dǎo)入AE要匹配坐標(biāo)的話要把Y軸乘以-1)

然后我們復(fù)制一個(gè)空物體,把它的父級(jí)設(shè)置成第一個(gè)創(chuàng)建的空物體,命名成Son,父級(jí)命名成Dad,再來看一下它的坐標(biāo)

雖然他們兩個(gè)的位置是重合的,但是子級(jí)的坐標(biāo)是[0,0],此時(shí)我們可以試一下隨意移動(dòng)父級(jí),子級(jí)的坐標(biāo)是不會(huì)變的
這在任何CG軟件里都是個(gè)很通用也很好理解的概念,其實(shí)現(xiàn)在Son是以Dad為原點(diǎn),無論Dad走到哪,Son的本地坐標(biāo)也不會(huì)變,想查看它的真實(shí)坐標(biāo),也就是它的全局坐標(biāo)的話,有幾種方法,最簡(jiǎn)單的方法就是直接把它解除父子級(jí)


當(dāng)然我們希望在不解除父級(jí)的情況下就能獲得子級(jí)準(zhǔn)確的坐標(biāo),這里可以使用一個(gè)方法,先在隨意一個(gè)圖層上建立一個(gè)Point Control,打開它的表達(dá)式窗口,寫入
thisLayer.toComp([0,0])

這時(shí)無論是否連接父子,這個(gè)Point Control都會(huì)顯示出來Son的全局坐標(biāo)位置了
當(dāng)然這里有個(gè)誤區(qū),我們的空物體錨點(diǎn)是在左上角,所以才能這樣獲得坐標(biāo)位置,在AE中并不只有全局坐標(biāo)和本地坐標(biāo),由于AE有三維和二維兩種空間,全局坐標(biāo)分為三維的世界空間和二維的合成空間兩種坐標(biāo)系統(tǒng),本地坐標(biāo)又直接存在于圖層空間中,相比三維軟件中的坐標(biāo)系統(tǒng)更加復(fù)雜,如果有機(jī)會(huì)的話再做一篇專欄詳細(xì)說明一下

下面正文
BB上面那些有啥意義呢,因?yàn)樨惾麪柷€的切線就是它的手柄,手柄有長(zhǎng)度有方向,我們通過輸入手柄的坐標(biāo)位置就可以決定手柄的長(zhǎng)度和方向,而這個(gè)坐標(biāo)位置就是相對(duì)于它所在的點(diǎn)的坐標(biāo),是一種局部坐標(biāo),再來回顧一下官方文檔中對(duì)切線數(shù)組的描述

也就是說手柄坐標(biāo)是以它所在的點(diǎn)為原點(diǎn),[x,y]表示相對(duì)位置的點(diǎn),我們實(shí)際來操作一下
建立一個(gè)形狀圖層,建立空Path,記得把位置屬性改為[0,0],先創(chuàng)建好連接Dad和Son的線段
注意由于Son有父級(jí),這里為了方便,我先把第二個(gè)點(diǎn)的變量連接剛才我們做好的Point Control的值來確保輸入的是全局位置

記得給個(gè)stroke方便能看到路徑
所以我們定義三個(gè)變量tansA、tansB、tansC,值分別是[0,0]、[100,0]、[-100,0],代表的是0長(zhǎng)度手柄、向右100單位長(zhǎng)度的手柄、向左100單位長(zhǎng)度的手柄(AE的一個(gè)單位就是一個(gè)像素,100單位長(zhǎng)度就是100像素)
因?yàn)槲覀冇謨蓚€(gè)點(diǎn),所以每個(gè)切線數(shù)組里也要有兩個(gè)點(diǎn),我們把分別把tansA、C,tansB、A裝到入切線和出切線的數(shù)組中,替換掉createPath中的inTangents和ouTangents的值,具體寫法在下面
PointA = thisComp.layer("Dad").transform.position;
PointB = thisComp.layer("Son").effect("Point Control")("Point");
cvs = [PointA, PointB];
tansA = [0,0];
tansB = [100,0];
tansC = [-100,0];
intans = [tansA,tansC];
outtans = [tansB,tansA];
createPath(points = cvs, inTangents = intans, outTangents = outtans, is_closed = false);

然后我們的曲線就有彎曲了,顯然現(xiàn)在切線是起了作用了,我們想要查看切線需要選中路徑,然后選擇在視圖中框選一下路徑的點(diǎn)

這個(gè)長(zhǎng)度也正好是我們定義的100的長(zhǎng)度,想讓手柄指向特定的方向只要我們定義相應(yīng)的變量就可以,或者我們像上面一樣給物體加一個(gè)Point Control的效果然后把點(diǎn)數(shù)值傳給切線變量,就可以在外面通過調(diào)點(diǎn)來控制手柄了
現(xiàn)在我只想要水平的手柄,所以我只添加一個(gè)滑塊控制來控制手柄長(zhǎng)度,并且把滑塊控制命名為Handles Length,我就可以通過這個(gè)滑塊來控制切線長(zhǎng)度了
PointA = thisComp.layer("Dad").transform.position;
PointB = thisComp.layer("Son").effect("Point Control")("Point");
cvs = [PointA, PointB];
handlesLength = effect("Handles Length")("Slider");
tansA = [0, 0];
tansB = [handlesLength, 0];
tansC = [-1 * handlesLength, 0];
intans = [tansA, tansC];
outtans = [tansB, tansA];
createPath(points = cvs, inTangents = intans, outTangents = outtans, is_closed = false);

然后可以用這個(gè)東西來做類似節(jié)點(diǎn)連接的動(dòng)畫(添加一點(diǎn)細(xì)節(jié))


仔細(xì)看的話我這個(gè)還能自動(dòng)吸附,能根據(jù)節(jié)點(diǎn)距離自動(dòng)調(diào)節(jié)切線長(zhǎng)度,具體制作方法大家自己研究就好了,很多軟件里的節(jié)點(diǎn)就是這樣的形式



當(dāng)然做成豎的斜的都行,下面我們用空物體控制手柄的方式繼續(xù)研究這個(gè)表達(dá)式,也不知道你看會(huì)了嗎,沒看會(huì)就假裝看會(huì)了吧

時(shí)間倒流一下,接著只有Dad和Son兩個(gè)物體的那步繼續(xù)做,就是下面這步的時(shí)候

我們?cè)傩陆ㄒ粋€(gè)空物體,命名為Dad outHandle, 然后在路徑表達(dá)式里把它的位置信息傳給一個(gè)新變量outHandleA,同樣建立一個(gè)切線數(shù)組outtans,把需要的切線都放進(jìn)去
記得我們有兩個(gè)點(diǎn),切線數(shù)組也需要有兩個(gè)值,我們暫時(shí)先把第二個(gè)值設(shè)定為[0,0],表示第二個(gè)點(diǎn)切線長(zhǎng)度是0,然后將這個(gè)切線數(shù)組替換掉原來的outTangents的值
PointA = thisComp.layer("Dad").transform.position;
PointB = thisComp.layer("Son").effect("Point Control")("Point");
cvs = [PointA, PointB];
outHandleA = thisComp.layer("Dad outHandle").transform.position;
outtans = [outHandleA, [0,0]];
createPath(points=cvs, inTangents=[], outTangents=outtans, is_closed=false);

顯然現(xiàn)在切線是起了作用了,為了查看切線我們需要選中path然后在視圖中框選曲線的點(diǎn)

這手柄位置顯然太長(zhǎng)了,完全不在Dad outHandle的位置上,解決這個(gè)問題之前我們先解決另一個(gè)問題,想看到切線就得選擇曲線點(diǎn),選擇了之后空物體就隱藏了,有個(gè)辦法就是選擇空物體然后在素材窗口按住alt拖拽空物體的素材替換掉合成中的空物體,然后把空物體的不透明度調(diào)高,就能看到了
因?yàn)锳E的空物體不可見,但是它在素材中只是個(gè)100*100的固態(tài)層,替換掉之后它就只是個(gè)固態(tài)層了,透明度屬性就可以起作用了

這時(shí)就能同時(shí)看到手柄和空物體的位置了,從這個(gè)位置不難看出來,它的手柄位置取了Dad outHandle的坐標(biāo)位置,變成了960*540,所以這個(gè)切線的真實(shí)位置并不是我們合成窗口的[960,540]的位置,而是以第一個(gè)點(diǎn)為原點(diǎn)的[960,540]的位置
那么要把它正確匹配到Dad outHandle的位置也很容易,把Dad outHandle的父級(jí)設(shè)置成Dad即可

這回得到了正確的手柄位置,移動(dòng)幾個(gè)空物體的位置試試,我們得到了一段曲線,并且它的手柄完全受我們控制
但是不要覺得萬事大吉,這只是我們?cè)谖恢蒙掀ヅ渖狭怂凶鴺?biāo),如果此時(shí)我們旋轉(zhuǎn)Dad的話

在旋轉(zhuǎn)父級(jí)的時(shí)候子級(jí)位置不會(huì)變化,而我們的手柄位置是直接取的子級(jí)本地坐標(biāo)的值,它沒有跟隨Dad Handle一起移動(dòng)
但不要擔(dān)心,仔細(xì)看發(fā)現(xiàn),雖然手柄沒有跟上物體位置,但是另一個(gè)端點(diǎn)卻好好的跟隨了Son的位置,這是因?yàn)槲覀儷@取的是Son的全局坐標(biāo)位置,與它的父物體無關(guān),我們可以也借此修正手柄的坐標(biāo)


哦豁,我們忘記了這里的手柄坐標(biāo)是基于曲線點(diǎn)為原點(diǎn)的,但是仔細(xì)觀察會(huì)發(fā)現(xiàn),切線手柄的位置其實(shí)就是把Dad Handle物體的位置與原點(diǎn)連成的線段移動(dòng)到了手柄位置與它所在點(diǎn)組成的線段上去了

這里又要先從正文出來一下,介紹一下向量這個(gè)東西(線性代數(shù)的最基本概念)
本來寫之前我覺得不用講這個(gè)的,但是寫著寫著感覺不對(duì)勁了,好像必須得說了

如果你學(xué)過線代或者用過houdini啥的經(jīng)常需要計(jì)算矢量的東西的話,估計(jì)你也用不到看這篇教程了,為了方便理解,我就從圖形的角度簡(jiǎn)單說一下好了,只要你知道坐標(biāo)系這個(gè)東西的話就應(yīng)該能理解得了,這里不會(huì)涉及到太難的東西,真的想了解線性代數(shù)的話可以搜索專業(yè)課程(諷刺的是我大學(xué)線代其實(shí)掛科了)

如果我們的空間中有一個(gè)點(diǎn)A的坐標(biāo)是[2,0],那么它在這個(gè)位置

[2,0]這個(gè)坐標(biāo)其實(shí)就是一個(gè)向量,它代表的是從原點(diǎn)指向A點(diǎn)的方向,以及長(zhǎng)度(和貝塞爾的切線坐標(biāo)的概念一樣吧),也就是從原點(diǎn)起步,往X軸方向移動(dòng)兩個(gè)單位,我們可以用一個(gè)箭頭來表示它

如果我們有另一個(gè)點(diǎn)B,坐標(biāo)是[0,1],代表它的向量自然是下圖這樣

B的向量就是在原點(diǎn)起步,向Y軸移動(dòng)一個(gè)單位的長(zhǎng)度
在正常情況下,向量的起點(diǎn)都是原點(diǎn),但是我們?cè)谥敖榻B父子級(jí)的時(shí)候,有個(gè)全局坐標(biāo)與局部坐標(biāo)的概念,也就是當(dāng)一個(gè)子級(jí)以父級(jí)為原點(diǎn)進(jìn)行變換,會(huì)怎么樣呢,我們讓B向量從A點(diǎn)開始起步,仍然向Y軸移動(dòng)一個(gè)單位

這時(shí)B點(diǎn)新的位置的坐標(biāo)很容易看出是[2,1],它與A、B兩點(diǎn)的坐標(biāo)有什么聯(lián)系呢,將A、B兩點(diǎn)的坐標(biāo)[2,0]、[0,1]X軸相加,Y軸也相加

我們得到一個(gè)與新B點(diǎn)完全匹配的坐標(biāo)[2,1],那么如果我們把AB交換一下,讓A從B的位置起步,沿X軸向右移動(dòng)2個(gè)單位呢

其實(shí)無論從平行四邊形的特性,還是數(shù)學(xué)的數(shù)值計(jì)算,還是路徑的首位相接各種概念來判斷,我們最后都會(huì)得到相同的結(jié)果
既然是平行四邊形,我們的向量并不需要橫平豎直,我們換一個(gè)斜著的方向來計(jì)算

神奇的是無論我們?nèi)绾巫儞Q基礎(chǔ)向量的方向,將他們相加之后總能得到與圖形匹配的相同結(jié)果,而且我們得到了一個(gè)新的向量C,它雖然也是從原點(diǎn)起步,但它是向量AB圍城的平行四邊形的對(duì)角線,而它的坐標(biāo)對(duì)應(yīng)了這個(gè)對(duì)角的點(diǎn)的坐標(biāo)
順便一提向量的乘法也很簡(jiǎn)單,[1,2]乘以2就是[2,4],想得到相反的向量只需要乘以-1就可以
好的,向量只需要知道這么一點(diǎn)點(diǎn)就夠了,足夠我們運(yùn)用在AE表達(dá)式中操作坐標(biāo)了
(如果你愿意深入了解數(shù)學(xué)和物理的話,會(huì)發(fā)現(xiàn)更多數(shù)字公式和圖形乃至物理中的量子在冥冥之中的相互聯(lián)系,堪比魔法一般令人難以置信,只怪我太垃圾,學(xué)不好數(shù)理化,最終成為一個(gè)臭做動(dòng)畫的)

那么聯(lián)系一下我們之前講過的父子級(jí),一個(gè)子級(jí)的坐標(biāo)是以父級(jí)為原點(diǎn)的,那我們把向量的計(jì)算方式帶入AE坐標(biāo)來看看,由于AE中一個(gè)單位是一個(gè)像素,雖然數(shù)值上沒有問題,但是肉眼很難看清物體在哪,所以我們把坐標(biāo)提到100或者1000這個(gè)量級(jí)上,建兩個(gè)空物體,隨便輸入坐標(biāo),然后連接父子級(jí)

然后我們ctrl D復(fù)制一個(gè)B,命名為C,解除它的父子級(jí),這樣C點(diǎn)雖然和B點(diǎn)重合,但是C顯示的是真實(shí)的在合成空間中的坐標(biāo)

多試幾個(gè)數(shù)值就會(huì)發(fā)現(xiàn),它完全能對(duì)應(yīng)向量的坐標(biāo)計(jì)算方式(注意AE的坐標(biāo)系是Y軸向下),這就是另一種獲得AE子級(jí)全局坐標(biāo)的方式,將它的本地坐標(biāo)直接與父級(jí)相加,就可以得到它的全局坐標(biāo)(如果父級(jí)還有父級(jí)的話還有再加上父級(jí)的父級(jí)(爺級(jí))),反過來它的全局坐標(biāo)減去本地坐標(biāo)也能得到父級(jí)的坐標(biāo)
注意這只是在父級(jí)沒有旋轉(zhuǎn)的情況下才能這樣算,物體的變換信息是個(gè)矩陣,包含位置旋轉(zhuǎn)縮放三個(gè)信息,只有另外兩個(gè)信息都是默認(rèn)數(shù)值的時(shí)候我們才可以直接加減坐標(biāo)數(shù)值(這也是為什么我們不能直接用空物體的本地坐標(biāo)傳給手柄坐標(biāo)的原因)

回到正文,現(xiàn)在回頭來看看我們的曲線手柄

這個(gè)平行四邊形已經(jīng)告訴我們?nèi)绾潍@得正確的手柄坐標(biāo)了,沒錯(cuò),我們只要把手柄物體的全局坐標(biāo)減去它所在點(diǎn)的全局坐標(biāo)就可以了
outHandleA = thisComp.layer("Dad outHandle").toComp([0, 0])-PointA;

這樣我們完美的控制曲線的點(diǎn)位置以及它的手柄位置,并且可以隨意組合控制點(diǎn)的物體和控制手柄的物體的父子關(guān)系,或者旋轉(zhuǎn)點(diǎn),都不會(huì)影響表達(dá)式對(duì)位置的判定

雖然我們想要把曲線圓滑很簡(jiǎn)單,大多數(shù)時(shí)候貌似也不太需要單獨(dú)控制切線,但是某些時(shí)候還是需要切線這個(gè)精確的控制方式的(大概)
比如這個(gè)類似正太分布的曲線,兩邊應(yīng)該要趨近于水平的

而下面這個(gè)不知道是什么的曲線,是我隨便生成的,我就叫他蘿莉曲線吧,盡管我已經(jīng)給它7個(gè)點(diǎn)了,中間的點(diǎn)都比兩端的點(diǎn)高,但它還是“出軌”了,而且曲率形態(tài)也不太好看,所以rotoBezier還是在對(duì)精確性沒有太高要求的時(shí)候適用
(還是帶把的正太好哇)

還有一點(diǎn)是手柄的切線讓曲線點(diǎn)有了旋轉(zhuǎn)的意義,rotoBezier的點(diǎn)只有位置信息是有效的,旋轉(zhuǎn)沒有任何作用,那么我們?nèi)绻谱饕粋€(gè)彎曲的箭頭的話,箭頭指向的方向可以用端點(diǎn)切線的方式精確匹配

或者類似管道這種有直也有彎的東西也是使用切線來生成更準(zhǔn)確點(diǎn)
我加了個(gè)Offset Paths效果器,可以把單線偏移出去變成雙層的線

當(dāng)然我們的目的不是生成這個(gè)曲線本身,直接畫出來效率更高,我們需要的是不對(duì)Path本身K幀的情況下精確控制它的動(dòng)畫形式

其實(shí)做到這里我也很想吐槽,我寫了一大坨就做了這么個(gè)傻B玩應(yīng)?

其實(shí)我本來還有些別的東西想展示的,但是寫到這里發(fā)現(xiàn)已經(jīng)6000多字了,可能是我寫得太墨跡了,所以后面的部分放到Part3吧(這么個(gè)鳥東西能寫到Part3我是沒想到的)
下一篇我們用這個(gè)表達(dá)式命令做一些程序化曲線,解決一些實(shí)際問題,比如不同大小齒輪的嚙合問題,還有...還有啥就再說吧,也可能啥不出來
