矩陣變換基礎(chǔ)--CALayer的transform擴展解析


概述:主要講述矩陣變換與圖形變化的聯(lián)系,逐步將矩陣應用到動畫編程,重點講述透視變換 M34;
參考文獻: (3D數(shù)學基礎(chǔ):圖形與游戲開發(fā) -- (美)Fletcher.Dunn)
原書購買地址:請尊重原創(chuàng)


一. 矩陣基礎(chǔ)

不做討論,請參閱[3D數(shù)學基礎(chǔ):圖形與游戲開發(fā) -- (美)Fletcher.Dunn),視跟人基礎(chǔ)選讀 Chapter7,Chapter8,Chapter9 即可,重點在矩陣乘法(X);


二.矩陣乘法的幾何意義:

矩陣乘法本質(zhì):矩陣乘法的本質(zhì)是線性空間運動的描述墓塌! 具體見下圖:

矩陣乘法幾何描述.jpg

對于矩陣乘法豪直,主要是考察一個矩陣對另一個矩陣所起的變換作用木人。其作用的矩陣看作是動作矩陣,被作用的矩陣可以看作是由行或列向量構(gòu)成的幾何圖形涡贱。同樣,如果一連串的矩陣相乘威兜,就是多次變換的疊加么坑夯。而矩陣左乘無非是把一個向量或一組向量(即另一個矩陣)進行伸縮或旋轉(zhuǎn)。乘積的效果就是多個伸縮和旋轉(zhuǎn)的疊加纽帖!比如S=ABCDEF會把所有的矩陣線性變化的作用力傳遞并積累下去宠漩,最終得到一個和作用力S。工業(yè)上的例子就是機器人的手臂懊直,機械臂上的每個關(guān)節(jié)就是一個矩陣(比如可以是一個旋轉(zhuǎn)矩陣)扒吁,機械臂末端的位置或動作是所有關(guān)節(jié)運動的綜合效果。這個綜合效果可以用旋轉(zhuǎn)矩陣的乘法得到室囊。

在code 的世界里,可以簡單理解為一個視圖A(矩陣A) 到另一個視圖B(矩陣B)的映射;


三. CALayer的transform擴展解析

在iOS 開發(fā)中已經(jīng)給出了部分API,我們重點關(guān)注 M34 的透視效果;

CATransform3DMakeTranslation
CATransform3DMakeScale
CATransform3DMakeRotation

繞坐標軸的旋轉(zhuǎn)場景:
如圖:

test

使用image.layer.transform = CATransform3DMakeRotation(M_PI/6, 0, 1, 0); 繞Y軸旋轉(zhuǎn)30度后的效果:
test

可以發(fā)現(xiàn)雕崩,繞Y軸旋轉(zhuǎn)只是在X軸上進行了縮放,這是因為融撞,在CALayer的顯示系統(tǒng)中盼铁,默認的相機使用正交投影,正交投影沒有遠小近大效果尝偎,所以在本例中饶火,只能造成相應軸上的縮放。在這種情況下致扯,無論是繞Y軸旋轉(zhuǎn)30度還是-30度都是同樣的效果趁窃。


透視投影

CALayer默認使用正交投影,因此沒有遠小近大效果急前,而且沒有明確的API可以使用透視投影矩陣醒陆。所幸可以通過矩陣連乘自己構(gòu)造透視投影矩陣。構(gòu)造透視投影矩陣的代碼如下:

CATransform3D CATransform3DMakePerspective(CGPoint center, float disZ){
   CATransform3D transToCenter = CATransform3DMakeTranslation(-center.x, -center.y, 0); 
   CATransform3D transBack = CATransform3DMakeTranslation(center.x, center.y, 0);
   CATransform3D scale = CATransform3DIdentity; scale.m34 = -1.0f/disZ; 
   return CATransform3DConcat(CATransform3DConcat(transToCenter, scale), transBack);
}

CATransform3D CATransform3DPerspect(CATransform3D t, CGPoint center, float disZ){ 
    return CATransform3DConcat(t, CATransform3DMakePerspective(center, disZ));
}

代碼中裆针,center指的是相機 的位置刨摩,相機的位置是相對于要進行變換的CALayer的來說的寺晌,原點是CALayer的anchorPoint在整個CALayer的位置,例如CALayer的大小是(320, 160), anchorPoint值為(0.5, 0.5)澡刹,此時anchorPoint在整個CALayer中的位置就是(160, 80)呻征,正中心的位置。傳入透視變換的相機位置為(0, 0)罢浇,那么相機所在的位置相對于CALayer就是(160, 80)陆赋。如果希望相機在左上角,則需要傳入(-160, -80)嚷闭。disZ表示的是相機離z=0平面(也可以理解為屏幕)的距離攒岛。
怎么解釋這段代碼呢?

  • 第一步胞锰,layer在CATransform3DPerspect方法中首先進行了t變換灾锯,要注意的是這時候進行的t變換是以anchorPoint為中心點,默認情況下是在layer的中心位置嗅榕。
  • 第二步顺饮,在CATransform3DMakePerspective方法先進行了一個(-center.x, -center.y, 0)的平移,然后進行了透視投影凌那,最后又做了一個(center.x, center.y, 0)平移兼雄。關(guān)鍵在于這兩個反向的平移。 當center為(0,0)也就是相機中心在CALayer的中心位置帽蝶,與anchorPoint(0.5, 0.5)重合時赦肋,平移的距離為0也就是沒有做平移,這時候是直接把已經(jīng)做過t變換的layer通過scale進行透視投影變換的嘲碱。

當center不在中心位置時,假設(shè)在CALayer的左上角局蚀,那么center為(-160, -80)麦锯。那center就平移到了(0,0)點,等于把相機點又平移回到了與anchorPoint重合的這一點琅绅,但由于平移此時這個重合點成矩形圖片的左上角了扶欣。那么為什么要平移至使相機點與anchorPoint點重合呢?
這里得明確一點千扶,相機點與layer同時在X-Y平面上做相同的偏移時料祠,因為沒有改變z值,在相機點看到的立體效果是相同的澎羞,只是相對原點的位置變動了而已髓绽。在相機點(-160, -80)看到的立體效果,就等效于在相機點(0,0)看到的把layer平移(160, 80)的立體效果.對一個layer來說妆绞,只要沒有修改anchorPoint顺呕,系統(tǒng)所認為的內(nèi)部相機點的投影是在anchorPoint這個位置枫攀,也就是相機點的(0,0)位置。因此要看到layer在相機點(-160, -80)透視投影的效果株茶,只能先作平移變換来涨,讓相機點與layer做相同的平移使相機點移到(0,0), 完成透視投影后再平移回去。

帶透視效果的繞Y軸旋轉(zhuǎn)启盛,效果如下:
test

相應的代碼為:
CATransform3D rotate = CATransform3DMakeRotation(M_PI/6, 0, 1, 0);
image.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 200);

可以發(fā)現(xiàn)在進行逆時針旋轉(zhuǎn)30度時蹦掐,在中心點左側(cè)的圖離相機點比較近,呈現(xiàn)出了比原圖大的效果僵闯,右側(cè)的圖離相機點比較遠卧抗,呈現(xiàn)出了比原圖小的效果。對比原圖棍厂,圖的左邊界超出了屏幕颗味,而右邊界在屏幕之內(nèi),這可以通過下面的這個圖來解釋:
test

圖中PQ為旋轉(zhuǎn)后的圖像在X-Z平面上的投影牺弹,相機點在O點浦马,從O點看過過,P张漂、Q兩點在X軸上的投影為C晶默、D點,C航攒、D兩點相對P磺陡、Q點在X軸上的原始位置都是靠左的,這也就解釋了旋轉(zhuǎn)后的透視投影效果左邊界超出了屏幕漠畜,右邊界在屏幕之內(nèi)币他。
上圖中A、B兩點是O點在無窮遠處所看到的P憔狞、Q兩點在X軸上的投影蝴悉,也就是我們在第二張圖片上看到的效果。這是因為在無窮遠處瘾敢,觀察P拍冠、Q時已經(jīng)失去了近大遠小的效果,所做的投影是正交投影簇抵。

如果把center設(shè)為(-160,-80),也就是把相機位置設(shè)為矩形圖片的左上角庆杜,則繞Y軸旋轉(zhuǎn)的透視效果如下:
test

立方體效果
CATransform3D可以使用CATransform3DConcat函數(shù)連接起來以構(gòu)造更復雜的變換, 通過這些方法,可以組合出更多的效果來碟摆。下面是個翻轉(zhuǎn)的動畫, 使用四張同樣大小的圖片圍成一個框晃财,讓這個框動畫旋轉(zhuǎn), 形成一個立方體旋轉(zhuǎn)的效果。
test

相關(guān)實現(xiàn)的代碼:

CATransform3D move = CATransform3DMakeTranslation(0, 0, 160);
CATransform3D back = CATransform3DMakeTranslation(0, 0, -160);

CATransform3D rotate0 = CATransform3DMakeRotation(-angle, 0, 1, 0);
CATransform3D rotate1 = CATransform3DMakeRotation(M_PI_2-angle, 0, 1, 0);
CATransform3D rotate2 = CATransform3DMakeRotation(M_PI_2*2-angle, 0, 1, 0);
CATransform3D rotate3 = CATransform3DMakeRotation(M_PI_2*3-angle, 0, 1, 0);

CATransform3D mat0 = CATransform3DConcat(CATransform3DConcat(move, rotate0), back);
CATransform3D mat1 = CATransform3DConcat(CATransform3DConcat(move, rotate1), back);
CATransform3D mat2 = CATransform3DConcat(CATransform3DConcat(move, rotate2), back);
CATransform3D mat3 = CATransform3DConcat(CATransform3DConcat(move, rotate3), back);

image0.layer.transform = CATransform3DPerspect(mat0, CGPointZero, 500);
image1.layer.transform = CATransform3DPerspect(mat1, CGPointZero, 500);
image2.layer.transform = CATransform3DPerspect(mat2, CGPointZero, 500);
image3.layer.transform = CATransform3DPerspect(mat3, CGPointZero, 500);

解析:要形成一個立方體旋轉(zhuǎn)的效果典蜕,首先需要構(gòu)造出一個立方體出來拓劝,怎么構(gòu)造呢雏逾?在這個例子里構(gòu)造的立方體是前后左右四個面的,如果把屏幕當做立方體的“前”面郑临,它的“左”栖博、“后”、“右”面我們是看不見厢洞,但是這三個面可以通過“前”面旋轉(zhuǎn)一個角度得到的:以立方體的中心點為支點仇让,將“前”面分別順時針旋轉(zhuǎn)90度、180度躺翻、270丧叽。因為屏幕寬度為320,這個立方體的中心點應在屏幕中心點后方160px的地方公你。
現(xiàn)在需要解決的一個問題就是:怎么實現(xiàn)以立方體的中心點為支點的旋轉(zhuǎn)踊淳。我們知道,在CALayer中l(wèi)ayer的旋轉(zhuǎn)是以anchorPoint為支點的陕靠,而這個anchorPoint并沒有在z軸上的維度迂尝,所以修改anchorPoint是不可能的,怎么辦呢剪芥?答案還是通過平移實現(xiàn)垄开,雖然不能修改anchorPoint,但我們可以改變圖片的位置税肪,將圖片往z軸正方向(靠近用戶的方向)平移160px的距離溉躲,這時候圖片與anchorPoint的相對位置,就等同于圖片在原始位置與立方體中心的相對位置益兄,它們所進行的旋轉(zhuǎn)效果是相同的锻梳,只是在z軸上的絕對距離不同。旋轉(zhuǎn)完成后净捅,再平移回去疑枯,即可達到繞立方體的中心點旋轉(zhuǎn)的效果。這也是變換矩陣mat0為什么要先進行z軸正方向160px平移灸叼,執(zhí)行rotate0旋轉(zhuǎn)之后又進行z軸負方向160px平移的緣故神汹。
要實現(xiàn)旋轉(zhuǎn)動畫庆捺,就需要動態(tài)改變這個立方體的繞y軸的角度古今,在這個例子里就是添加了一個動態(tài)變化的angle達到這個目的。另外注意此例的旋轉(zhuǎn)是繞y軸旋轉(zhuǎn)的滔以,根據(jù)此前一篇文章的判斷方法捉腥,此時旋轉(zhuǎn)的正方向應該是z軸正方向頂點指向x軸正方向頂點,從用戶眼睛看來就是逆時針你画。如果angle是遞增的抵碟,那么-angle就是遞減的桃漾,因此實際看到的旋轉(zhuǎn)動畫會是順時針的

也許分析完又有了疑問,對一個layer做平移,會修改它的anchorPoint和position嗎拟逮?很顯然撬统,對旋轉(zhuǎn)和綻放必須要有一個固定的支點,感覺上平移不需要支點也行敦迄,是不是就會修改anchorPoint呢恋追?答案是否定時,簡單做一下測試罚屋,就知道layer在做平移時苦囱,anchorPoint和position都不會改變,frame會變化脾猛,說明frame不僅受anchorPoint和position影響撕彤,還受translation影響.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市猛拴,隨后出現(xiàn)的幾起案子羹铅,更是在濱河造成了極大的恐慌,老刑警劉巖漆弄,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件睦裳,死亡現(xiàn)場離奇詭異,居然都是意外死亡撼唾,警方通過查閱死者的電腦和手機缓呛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伞梯,“玉大人衣式,你說我怎么就攤上這事〔吵睿” “怎么了牵祟?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長抖格。 經(jīng)常有香客問我诺苹,道長,這世上最難降的妖魔是什么雹拄? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任收奔,我火速辦了婚禮,結(jié)果婚禮上滓玖,老公的妹妹穿的比我還像新娘坪哄。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布翩肌。 她就那樣靜靜地躺著模暗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪念祭。 梳的紋絲不亂的頭發(fā)上兑宇,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機與錄音粱坤,去河邊找鬼顾孽。 笑死,一個胖子當著我的面吹牛比规,可吹牛的內(nèi)容都是我干的若厚。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼蜒什,長吁一口氣:“原來是場噩夢啊……” “哼测秸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起灾常,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤霎冯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后钞瀑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沈撞,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年雕什,在試婚紗的時候發(fā)現(xiàn)自己被綠了缠俺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡贷岸,死狀恐怖壹士,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情偿警,我是刑警寧澤躏救,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站螟蒸,受9級特大地震影響盒使,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜七嫌,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一少办、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧抄瑟,春花似錦凡泣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惹资,卻和暖如春贺纲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背褪测。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工猴誊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人侮措。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓懈叹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親分扎。 傳聞我的和親對象是個殘疾皇子澄成,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容