变换

仿射变换

CGAffinieTransform 中的"仿射"的意思是无论变换矩阵用什么值,图层中的平行的两条线在变换后任然保持平行。

创建一个CGAffineTransform:

CGAffineTransformMakeRotation(CGFloat angle)

CGAffineTransformMakeScale(CGFloat sx,CGFloat sy)

CGAffineTransformMakeTranslation(CGFloat tx,CGFlaot ty)

UIView 可以通过设置 transform 属性做变换,但实际上它只是封装了内部图层的变换。

CALayer 同样也有一个 transform 属性,但它的类型是 CATransform3D, 而不是 GCAffineTransform。CALayer对应UIView的transform 属性叫做  affineTransform。

CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);

self.layerView.layer.affineTransform = transform;


混合变换

CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)

CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)

CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)

当操作一个变量的时候,初始生成一个什么都不做的变换也很重要,即CGAffineTransform 类型的空值

CGAffineTransformIdentity

如果需要混合两个已经存在的变换矩阵,可以使用如下方法,在两个变换的基础上创建一个新的变换:

CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2)

例:先缩小50%,再旋转30度,最后向右移动200像素

CGAffineTransform transform = CGAffineTransformIdentity;

transform = CGAffineTransformScale(transform, 0.5, 0.5);

transform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30);

transform = CGAffineTransformslate(transform, 200, 0)

self.view.layer.affineTransform = transform;

注意:图片向右发生了平移,但是没有指定距离那么远。另外他还有点向下发生了平移。原因在于当你按顺序做了变换,上一个变换的结果将会影响之后的变换。这就意味着变换的顺序会影响最终的结果。也就是说先平移后旋转和先旋转后平移结果可能不同。


3D变换

CATransform3DMakeRotation(CGFloat angle, CGFloat x,  CGFloat y,  CGFloat z)

CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)

CATransform3DMakeTranslation(CGFloat tx, CGFloat ty, CGFloat tz)


透视投影

CATransform3D 的透视效果通过一个矩阵中的一个很简单的元素来控制: m34。m34 用于按比例缩放X和Y的值来计算到底要离视角多远。

m34 的默认值是0, 我们可以通过设置 m34 为 -1.0 / d 来应用透视效果。d 代表了详细中的视角相机和屏幕直接的距离。以像素为单位。

实际上这个值并不重要,大概估算一个就好了。因为视角相机实际上并不存在,所以可以根据屏幕上的显示效果自由决定它的放置位置。通常 500 - 1000 就已经很好了。减少距离的值会增强透视效果。

CATransform3D transform = CATransform3DIdentity;

transform.m34 = -1.0 / 500;

transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);

self.layerView.layer.transform = transform;


灭点

当在透视角度绘图的时候,原理相机视角的物体将会变小变远,当远离到一个极限距离,他们可能就缩成了一个点,于是所有的物体最后都汇聚消失在同一个点。

在现实中,这个点通常是视图中心。于是为了应用中创建拟真效果的透视,这个点应该聚在屏幕中心,或者至少是包含所有3D对象的视图中心。

Core Animation 定义了这个点位于变换图层的 anchorPoint(通常位于图层中心,但也有例外)。这就是说,当图层发生变换时,这个点用于位于图层变换之前 anchorPoint 的位置。

当改变一个图层的 position 时,你也改变了它的灭点,做3D 变换的时候要时刻记住这一点,当你试图通过调整 m34 来让它更加有3D效果,应该首先把它放置于屏幕的中央,然后通过平移来把它移动到指定位置。(而不是直接改变它的 position),这样所有的3D 图层都共享一个灭点。

sublayerTransform 属性

如果有多个视图或图层,每个都做3D变换,那就需要分别设置相同的 m34 值,并且确保在变换之前都是在屏幕中央共享同一个 position,如果用一个函数封装这些操作的确会更加方便,但仍然有限制(如不能再XIB种摆放视图),这里有个更好的方法。

CALayer 有个属性叫做 sublayerTransform。它也是 CATransform3D 类型,但和对一个图层的变换不同,它影响到所有的子图层。这意味着你可以一次性对包含这些图层的容器做变换。于是所有的子图层都自动继承了这个变换方法。灭点被设置在容器图层的中点,从而不需要在对子图层分别设置了。

CATransform3D perspective = CATransform3DIdentity;

perspective.m34 = -1.0 / 500.0;

self.containerView.layer.sublayerTransform = perspective;

CATransfrom3D transform1 = CATransform3DMakeRotation(M_PI_4, 0, 0, 0);

self.layerView1.layer.transform = transform1;

CATransform3D transform2 = CATransform3DMakeRotation(-M_PI_4, 0, 0, 0);

self.layerView2.layer.transform = transform2;


背面

图层是双面绘制的,反面显示的是正面的一个镜像图片。

CALayer 有一个叫做 doubleSided 的属性来控制图层的背面是否要被绘制。这是一个 BOOL 类型,默认为YES。如果设置为NO,那么当图层正面从相机视角消失的时候,它将不会被绘制。(既然不看背面,为何要浪费GPU绘制呢?资源浪费)


扁平化图层

尽管Core Animation 图层存在于3D空间之内,但他们并不都存在同一个3D空间,每个图层的3D场景其实都是扁平化的,当你从正面观察一个图层,看到的实际上由子图层创建的想象出来的3D场景。但当你倾斜这个图层,你会发现实际上这个3D场景仅仅是被绘制在图层的表面。


固体对象

- (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform {

    UIView*face =self.faces[index];

    [self.containerViewaddSubview:face];

    CGSizecontainerSize =self.containerView.bounds.size;

    face.center=CGPointMake(containerSize.width/2.0, containerSize.height/2.0);

    face.layer.transform= transform;

}

- (void)viewDidLoad  {

    [super viewDidLoad];

    CATransform3D perspective = CATransform3DIdentity;

    perspective.m34= -1.0/500.0;

    perspective = CATransform3DRotate(perspective, -M_PI_4,1,0,0);

    perspective = CATransform3DRotate(perspective, -M_PI_4,0,1,0);

    self.containerView.layer.sublayerTransform = perspective;

    CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);

    [self addFace:0 withTransform:transform];

    transform =CATransform3DMakeTranslation(100, 0, 0);

    transform =CATransform3DRotate(transform,M_PI_2,0,1,0);

    [self addFace:1 withTransform:transform];

    transform =CATransform3DMakeTranslation(0, -100, 0);

    transform =CATransform3DRotate(transform,M_PI_2,1,0,0);

    [self addFace:2 withTransform:transform];

    transform =CATransform3DMakeTranslation(0, 100, 0);

    transform =CATransform3DRotate(transform, -M_PI_2,1,0,0);

    [self addFace:3 withTransform:transform];

    transform =CATransform3DMakeTranslation(-100, 0, 0);

    transform =CATransform3DRotate(transform, -M_PI_2,0,1,0);

    [self addFace:4 withTransform:transform];

    transform =CATransform3DMakeTranslation(0, 0, -100);

    transform =CATransform3DRotate(transform,M_PI,0,1,0);

    [self addFace:5 withTransform:transform];

}


光亮和阴影

 - (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform {

        .......

        [self applyLightingToFace:face.layer];

}

- (void)applyLightingToFace:(CALayer*)face {

    CALayer*layer = [CALayerlayer];

    layer.frame= face.bounds;

    [face addSublayer:layer];

    CATransform3Dtransform = face.transform;

    GLKMatrix4matrix4 = *(GLKMatrix4*)&transform;

    GLKMatrix3matrix3 =GLKMatrix4GetMatrix3(matrix4);

    GLKVector3normal =GLKVector3Make(0,0,1);

    normal =GLKMatrix3MultiplyVector3(matrix3, normal);

    normal =GLKVector3Normalize(normal);

    GLKVector3 light = GLKVector3Normalize(GLKVector3Make(0, 1, -0.5));

    floatdotProduct =GLKVector3DotProduct(light, normal);

    CGFloatshadow =1+ dotProduct -0.5;

    UIColor*color = [UIColorcolorWithWhite:0alpha:shadow];

    layer.backgroundColor = color.CGColor;

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容