iOS-從三維立方體到理解CATransform3D&CGAffineTransform&m34

前言

在寫Custom Layout的demo時,用到了CATransform3D的m34參數,遊手好閒的想探究下這個矩陣到底爲何能影響到圖形的透視旋轉等等變換,因此經過本篇文章總結一下收穫,供之後參考html

目錄app

  • 簡單實現三維立方體
  • CATransform3D&CGAffineTransform使用介紹
  • 原理探究及理解

簡單實現三維立方體

Cube.gif

實現這個蠻簡單的,只須要合理的調整旋轉角度和平移,再加上一個定時器,完美(顯然這個效果沒什麼卵用,可是筆者這隻鶸來講,剛蹦出來的時候仍是蠻開心的)ide

實現步驟 : 1.定義一個Basic View -> 2.調整並添加立方體的六個面 3.定時器調整Basic View的layer旋轉
特別注意:旋轉時,咱們須要同時做用於6個子layer,因此請留意self.animateCube.layer.sublayerTransform = transform中使用的是sublayerTransform而非transformoop

CGRect targetBounds = (CGRect){CGPointZero,CGSizeMake(200, 200)};
    self.animateCube = [[UIView alloc] initWithFrame:targetBounds];
    _animateCube.center = self.view.center;
    [self.view addSubview:self.animateCube];
    
    
    UIView *test = [[UIView alloc] initWithFrame:targetBounds];// front
    test.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.25];
    test.layer.transform = CATransform3DTranslate(test.layer.transform, 0, 0, 100);
    
    UIView *test1 = [[UIView alloc] initWithFrame:targetBounds];// back
    test1.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
    test1.layer.transform = CATransform3DTranslate(test1.layer.transform, 0, 0, -100);
    
    UIView *test2 = [[UIView alloc] initWithFrame:targetBounds];// left
    test2.backgroundColor = [[UIColor yellowColor] colorWithAlphaComponent:0.5];
    test2.layer.transform = CATransform3DTranslate(test2.layer.transform, -100, 0, 0);
    test2.layer.transform = CATransform3DRotate(test2.layer.transform, M_PI_2, 0, 1, 0);
    
    UIView *test3 = [[UIView alloc] initWithFrame:targetBounds];// right
    test3.backgroundColor = [[UIColor purpleColor] colorWithAlphaComponent:0.5];
    test3.layer.transform = CATransform3DTranslate(test3.layer.transform, 100, 0, 0);
    test3.layer.transform = CATransform3DRotate(test3.layer.transform, M_PI_2, 0, 1, 0);
    
    UIView *test4 = [[UIView alloc] initWithFrame:targetBounds];// head
    test4.backgroundColor = [[UIColor orangeColor] colorWithAlphaComponent:0.5];
    test4.layer.transform = CATransform3DTranslate(test4.layer.transform, 0, 100, 0);
    test4.layer.transform = CATransform3DRotate(test4.layer.transform, M_PI_2, 1, 0, 0);
    
    UIView *test5 = [[UIView alloc] initWithFrame:targetBounds];// foot
    test5.backgroundColor = [[UIColor greenColor] colorWithAlphaComponent:0.5];
    test5.layer.transform = CATransform3DTranslate(test5.layer.transform, 0, -100, 0);
    test5.layer.transform = CATransform3DRotate(test5.layer.transform, M_PI_2, -1, 0, 0);

    [self.animateCube addSubview:test];
    [self.animateCube addSubview:test1];
    [self.animateCube addSubview:test2];
    [self.animateCube addSubview:test3];
    [self.animateCube addSubview:test4];
    [self.animateCube addSubview:test5];
    
    self.animateCube.transform = CGAffineTransformMakeScale(0.5, 0.5);//CGAffineTransform
    
    __block CATransform3D transform = CATransform3DIdentity;

    NSLog(@"%@",[NSString logForCATransform3D:transform]);

    // Label
    UILabel *label = [[UILabel alloc] init];
    label.frame = CGRectOffset(self.animateCube.frame, 0, - 100);
    label.text = @"AnimatedCube";
    [label sizeToFit];
    [self.view addSubview:label];

    transform.m34 = 1.0/-500;
    
    float angle = M_PI / 360;
    self.animateCube.layer.sublayerTransform = transform;
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0/60 repeats:YES block:^(NSTimer * _Nonnull timer) {
        transform = CATransform3DRotate(transform, angle, 1, 1, 0.5);
        self.animateCube.layer.sublayerTransform = transform;//
    }];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

CATransform3D&CGAffineTransform使用介紹

CGAffineTransform

CGAffineTransform.png

CGAffineTransform學習

An affine transformation matrix for use in drawing 2D graphics
用於繪製2D圖形的仿射變換矩陣動畫

以上爲官方文檔定義,那麼什麼是仿射變換,看看這裏數學咖們的解釋就能大概理解了→如何通俗地講解「仿射變換」這個概念?this

  1. CGAffineTransformMake返回直接控制每個參數的仿射變換矩陣
CGAffineTransform CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty);
  1. CGAffineTransformScale 返回經過縮放現有仿射變換構造的仿射變換矩陣,sx/sy即爲x方向y方向的縮放比例
CGAffineTransform CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy);
  1. CGAffineTransformRotate返回經過旋轉現有仿射變換構造的仿射變換矩陣,angle爲旋轉弧度
CGAffineTransform CGAffineTransformRotate(CGAffineTransform t, CGFloat angle);
  1. CGAffineTransformInvert返回經過反轉現有仿射變換構造的仿射變換矩陣。
CGAffineTransform CGAffineTransformInvert(CGAffineTransform t);
  1. CGAffineTransformTranslate 返回實現平移的仿射變換矩陣。tx/ty爲偏移量
CGAffineTransform CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty);
  1. CGAffineTransformConcat 返回經過組合兩個現有仿射變換構造的仿射變換矩陣。本質就是兩個矩陣的乘法,上述平移、旋轉、縮放的操做,如配圖所示,都是能夠經過點的齊次座標與仿射變換矩陣的乘積得到,原理筆者會在第三部分解釋
CGAffineTransform CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);
  1. 幾個特殊的仿射變換矩陣
    CGAffineTransformMakeScale/CGAffineTransformMakeRotation /CGAffineTransformMakeTranslation 都是在原視圖初始座標的基礎上變化,因此並不會累加效果,相比上述Api它少了基礎仿射矩陣參數CGAffineTransform t
// scale
CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy);
// rotaion
CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle);
// translation
CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty);
  1. CGAffineTransformIdentity 單位矩陣以下圖所示,他並不會對圖形形成任何影響,一般用來恢復初始狀態

CGAffineTransformIdentity.png

咱們要想將文字視圖翻轉該怎麼作呢?
2D翻轉
很簡單,只須要將CGAffineTransformMakeScale中的參數設置爲-1便可。是否是頗有趣spa

label.transform = CGAffineTransformMakeScale(-1, -1);

CATransform3D

CATransform3D.png

CATransform3D3d

Defines the standard transform matrix used throughout Core Animation.
定義核心動畫中使用的標準變換矩陣code

CATransform一樣定義爲結構體,表明了三維圖形4x4的變換矩陣。網上有幾篇文章,對每一個參數功能都進行了標註,可是這樣並不十分嚴謹。由於左上3x3的矩陣區域,各個參數須要相互做用才能達到理想的準確狀態,並不單純的是某個值負責某種準確的三維圖形變換。筆者將在本文的第三部分原理探究及理解中具體解釋,感(hen)興(wu)趣(liao)的同窗能夠往下看看。

由於只是從2D變換變成3D變換,並且4x4矩陣自己就是3x3矩陣的拓展,原理是同樣的,因此有不少類似相通的地方。

  1. CATransform3DScale返回經過縮放現有變換構造的變換矩陣,sx/sy/sz即爲x方向y方向z方向的縮放比例
CATransform3D CATransform3DScale (CATransform3D t, CGFloat sx,
    CGFloat sy, CGFloat sz)
  1. CATransform3DRotate返回經過旋轉現有變換構造的變換矩陣,angle表明弧度,x,y,z表明各個軸上旋轉的弧度倍數
CATransform3D CATransform3DRotate (CATransform3D t, CGFloat angle,
    CGFloat x, CGFloat y, CGFloat z)
  1. CATransform3DInvert返回反轉後的變換矩陣
CATransform3D CATransform3DInvert (CATransform3D t)
  1. CATransform3DTranslate返回實現x/y/z軸上平移相應距離的變換矩陣
CATransform3D CATransform3DTranslate (CATransform3D t, CGFloat tx,
    CGFloat ty, CGFloat tz)
  1. CATransform3DConcat返回同時做用兩種變換矩陣的矩陣
CATransform3D CATransform3DConcat (CATransform3D a, CATransform3D b)
  1. 幾個特殊的變換矩陣
    CATransform3DMakeScale/CATransform3DMakeRotation /CATransform3DMakeTranslation 一樣是做用於原始視圖的變換矩陣
/* Returns a transform that translates by '(tx, ty, tz)':
 * t' =  [1 0 0 0; 0 1 0 0; 0 0 1 0; tx ty tz 1]. */

CATransform3D CATransform3DMakeTranslation (CGFloat tx,
    CGFloat ty, CGFloat tz)

/* Returns a transform that scales by `(sx, sy, sz)':
 * t' = [sx 0 0 0; 0 sy 0 0; 0 0 sz 0; 0 0 0 1]. */

CATransform3D CATransform3DMakeScale (CGFloat sx, CGFloat sy,
    CGFloat sz)

/* Returns a transform that rotates by 'angle' radians about the vector
 * '(x, y, z)'. If the vector has length zero the identity transform is
 * returned. */

CATransform3D CATransform3DMakeRotation (CGFloat angle, CGFloat x,
    CGFloat y, CGFloat z)
  1. CATransform3DIdentity[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1],人畜無害矩陣,一般用於恢復初始狀態

原理解釋

CGAffineTransform

如下是蘋果官方文檔對於CGAffineTransform二維變換矩陣對圖形影響的註解

CGAffineTransform官方文檔註解An affine transformation matrix is used to rotate, scale, translate, or skew the objects you draw in a graphics context. The CGAffineTransform
type provides functions for creating, concatenating, and applying affine transformations.
Affine transforms are represented by a 3 by 3 matrix:

A 3 by 3 matrix.

Because the third column is always (0,0,1), the CGAffineTransform
data structure contains values for only the first two columns.
Conceptually, an affine transform multiplies a row vector representing each point (x,y) in your drawing by this matrix, producing a vector that represents the corresponding point (x’,y’):

A row vector multiplying a 3 by 3 matrix.

Given the 3 by 3 matrix, the following equations are used to transform a point (x, y) in one coordinate system into a resultant point (x’,y’) in another coordinate system.

Transformation equations.

看到這裏,線代大神必定會嘴角上揚了,因此若是如下內容有任何理解或書寫錯誤,請您務必留言給筆者鶸渣評論勘誤打臉,千萬不要高擡貴手。

二維的變換均可以看做是座標的齊次座標同[a b 0; c d 0; tx ty 1]的乘法運算

矩陣乘法圖示-阮一峯博客

矩陣的乘法規則:第m行與第n行交叉位置的值,等於第一個矩陣第m行與第二個矩陣第n列對應位置的乘積之和。依據這個規則,再看下圖,是否是感受豁然開朗
image.png
知道了原理後,咱們繼續探究translate/scale/rotate到底「背地裏」幹了些什麼

上文中咱們提到了,單獨對結構體中某個的參數(Translate平移的參數固然不會出錯,問題集中在對於線性變化區域的標註,即CGAffineTransform左上2x2區域CATransform3D左上3x3區域)進行功能註釋是不夠嚴謹的。

爲了證實這一觀點,筆者繪製了CGAffineTransformRotate 仿射矩陣的推導過程圖(這一結論在CATransform3DRotate矩陣中,一樣適用,證實具體參數標註的不嚴謹
CGAffineTransformRotate的仿射矩陣推導

  • 相關推導用到的轉換公式
sin(A+B) = sinAcosB + cosAsinB 
sin(A-B) = sinAcosB - cosAsinB
cos(A+B) = cosAcosB - sinAsinB 
cos(A-B) = cosAcosB + sinAsinB

下面給出相對簡單的CGAffineTransformTranslate CGAffineTransformScale 的矩陣的簡單推導

CGAffineTransformTranslate

CGAffineTransformScale

細心的同窗可能會發現,系統Api修改的都是矩陣的前兩列參數。那麼調整第三列,會有什麼樣的效果?

第三列參數,直接做用到齊次座標的n+1維的參數上,它同普通座標的轉換關係以咱們一直討論的二維爲例,以下圖。爲保證x = x',y = y',因此默認轉換後,齊次座標的d = 1,若是第三列前兩個參數均爲0,那麼修改右下位置的參數,能夠直接控制圖形的縮放。前兩個參數則是隨着圖形中點的x、y值的變化改變對於齊次座標中d值得影響。

不過蘋果並無給咱們提供這三個參數,而是選擇在二維變換時把這一列去掉了-。- 可能以爲意義不大吧。可是,在CATransform3D中,這一列獲得了保留,咱們熟悉的m34,正是受z軸影響的透視關鍵參數,相信看到這裏的你,應該已經可以理解爲何改變m34可以影響屏幕座標中的透視關係了。

CATransform3D

3D矩陣變換,其實就是2D矩陣變換的拓展,相應的3x3矩陣變成CATransform3D表明的4x4矩陣

通過對於CGAffineTransform的學習,這裏咱們就再也不推導經常使用的3D Api對應的變換了,直接貼上結果圖
相關矩陣

變換矩陣根據功能劃分爲四個區域,這裏不作T一、T3區域的單獨標註,觀點在上文中已經提出,再也不贅述
變換矩陣功能區分

  • 投影變換

目前大部分電子設備的圖形顯示,都是隻能用二維圖形表示三維物體,所以三維物體就要靠投影來下降位數獲得二維平面圖形,3D到2D轉換的過程,稱爲投影變換

  • m34爲何能改變透視關係
    m34影響到了T4區域的s值s值會對投影的圖形在z軸方向產生線性影響,爲何一般會用1/d負值,由於用戶特殊的觀察視角,決定了感官上近大遠小的特性,同時iOS中的座標系,是左手座標系,遠離咱們的方向,是z軸的負方向,因此越深刻屏幕,圖像中點的齊次座標中的d就越大,屏幕上的投影就越小
  • 旋轉正方向的肯定:左手座標系適用於左手定律(反之亦然),握住指定軸,拇指指向該軸正方向,四指指向方向即爲正方向

博客地址:Tr2e的博客
簡書地址:Tr2e的簡書

相關文章
相關標籤/搜索