Unity動畫的關鍵幀插值有2種模式,一種是3次多項式插值,另一種是3次貝塞爾曲線插值。下面介紹具體實現,首先我們先看關鍵幀的幾個重要成員定義:
inTangent,左側的斜率,下面用it來表示
inWeight,決定貝塞爾曲線的第2個點的x,用iw表示
outTangent,右側的斜率,用ot表示
outWeight,決定貝塞爾曲線第3個點的x,用ow表示
value,關鍵幀的值,用y表示
time,關鍵幀的時間,用x表示
給定兩個關鍵幀,
Fi={iti,iwi,oti,owi,xi,yi}, 0≤i≤1
三次多項式插值
三次多項式插值在兩個關鍵幀的weightedMode都是None
時使用。
三次多項式方程爲
y=f(x)=ax3+bx2+cx+d
我們可以使用如下4個方程解得4個係數
⎩⎪⎪⎪⎨⎪⎪⎪⎧f(x0)=y0f(x1)=y1f′(x0)=ot0f′(x1)=it1
得到的結果以矩陣表示爲
⎣⎢⎢⎡abcd⎦⎥⎥⎤=⎣⎢⎢⎡x03x133x023x12x02x122x02x1x0x1111100⎦⎥⎥⎤−1⎣⎢⎢⎡y0y1ot0it1⎦⎥⎥⎤
計算逆矩陣比較麻煩,我們可以將曲線的區間平移並規格化到[0,1]中再做計算,這樣就方便許多,新的方程組爲
⎩⎪⎪⎪⎨⎪⎪⎪⎧f(0)=y0f(1)=y1f′(0)=ot0(x1−x0)f′(1)=it1(x1−x0)
注意,由於進行了縮放,端點處的斜率也需要進行縮放。
解得
⎣⎢⎢⎡abcd⎦⎥⎥⎤=⎣⎢⎢⎡(ot0+it1)(x1−x0)−2(y1−y0)(−2ot0−it1)(x1−x0)+3(y1−y0)ot0(x1−x0)y0⎦⎥⎥⎤
給定時間t,我們可以使用如下公式得到插值的結果
f(x1−x0t−x0)
我們驗證一下結果,給定兩個關鍵幀
F0={0,31,0,31,0,0}F1={0,31,0,31,1,1}
該多項式實際爲
y=−2x3+3x2
對glsl熟悉的朋友應該會對這個多項式不陌生,就是smoothstep所用hermite多項式。
unity中的曲線爲
在unity中查看1/3,2/3處的結果爲
我們再看一下代入多項式後的值
f(31)=277=0.2592592593f(32)=2720=0.7407407407
結果是一致的。
貝塞爾曲線插值
多項式插值不使用inWeight
和outWeight
,根據unity文檔
Sets the incoming weight for this key. The incoming weight affects the slope of the curve from the previous key to this key.
第一想法是使用了貝塞爾曲線一類的插值方法。
三次貝塞爾曲線需要4個點,而這裏4個點的定義如下
P0P1P2P3=(x0,y0)=(x0+ow0(x1−x0),y0+ow0⋅ot0(x1−x0)=(x1−iw1(x1−x0),y1−iw1⋅it1(x1−x0)=(x1,y1)
這裏iw和ow用來決定中間點的x座標,且權重在[0,1]內。
我們來驗證一下,給定如下兩個關鍵幀(我們需要將Tangent設置爲Weighted)
F0F1={0,31,−1,1,0,0}={0,31,−0.5,0.5,1,1}
unity中的曲線爲
在unity中查看1/3,2/3處的結果爲
由於貝塞爾曲線不是以x爲參數的曲線,我們需要先解得對應參數t,之後再代入得到y。三次方程有封閉解,具體見Wikipedia。
解得對應的兩個t以及y爲
t0y0t1y1=0.1371916262=−0.2429122496=0.4502223408=0.1009137022
可以看到與editor中的結果一致。
默認權重
當一個關鍵幀的weightedMode
不是weighted
時,此時會默認採用1/3作爲權重,該權重爲unity默認寫入的值,通過觀察clip文件可知。