矩阵乘法
- 矩阵C的行数等于矩阵A的行数,C的列数等于B的列数。
- 乘积C的第m行第n列的元素等于矩阵A的第m行的元素与矩阵B的第n列对应元素乘积之和。
矩阵变换原理
- 通过矩阵对图层位图进行平移、旋转、缩放变换。
- 矩阵相乘只有在第一个矩阵的列数(column)和第二个矩阵的行数(row)相同时才有意义。
- 图层的bitmap由点组成,每个点可以对应1×4矩阵,乘以一个4×4变换矩阵,得到一个1×4矩阵,即为变换后的结果。
思考:
- 点坐标为什么要转换为1×4矩阵?
- 变换矩阵为什么必须是4×4矩阵?
- 如何实现移动,缩放,旋转?
如果只使用3×3变换矩阵:
m11, m12, m13
{x, y, z} * { m21, m22, m23 } = {x', y', z'}
m31, m32, m33
那么xˊ=x × m11 + y × m21 + z × m31
,在预先不对变量系数(m11, m21, m31)做其他计算的情况下,只能实现在各个坐标轴的缩放
但是使用使用1×4齐次矩阵和4×4变换矩阵后,xˊ= x × m11 + y × m21 + z × m31 + 1 × m41
。比如当m11=2 m21=0 m31=0 m41=8时,可同时实现向x轴正方向放大2倍,在沿着x轴正方向平移8个单位
使用1×4矩阵,是相对点的三维坐标进行齐次坐标。齐次坐标就是将一个原本是n维的向量用一个n+1维向量来表示:
齐次坐标变换 (x, y, z) -> (x × h, y × h, z × h, h) -> (xˊ, yˊ, zˊ, h)
齐次坐标还原 (xˊ, yˊ, zˊ, h) -> (x / h, y / h, z / h, 1) -> (x, y, z)
引入齐次坐标的目的主要是合并矩阵运算中的乘法和加法。
基本变换矩阵
矩阵就是利用矩阵内特殊位置的值,在做矩阵乘法时,达到对点坐标进行变换,下面时常用变换矩阵
平面仿射变换
CGAffineTransform中的“仿射”的意思是无论变换矩阵用什么值,图层中平行的两条线在变换之后任然保持平行,即纯粹2D变换,没有透视效果。
先来观察平面变换中的矩阵结构:
struct CGAffineTransform
{
CGFloat a, b, c, d;
CGFloat tx, ty;
};
为了把二维图形的变化统一在一个坐标系里,引入了齐次坐标的概念,即把一个图形用一个三维矩阵表示,其中第三列总是(0,0,1),用来作为坐标系的标准。所以所有的变化都由前两列完成。
以上参数在矩阵中的表示为:
|a b 0|
|c d 0|
|tx ty 1|
运算原理:原坐标设为(X,Y,1);
|a b 0|
[X,Y, 1] |c d 0| = [aX + cY + tx bX + dY + ty 1] ;
|tx ty 1|
可见,仿射变换忽略了z坐标,实现了2D变换:旋转、平移、缩放。但是要实现3D变换的话,还要了解透视原理。
透视效果
在真实世界中,当物体远离我们的时候,由于视角的原因看起来会变小,即透视效果--近大远小。
那该如何产生近大远小呢?
要达到近大远小目的,需要在系统做垂直投影前,先对图层做一次视点变换。如此垂直投影别是视点观察到的近大远小的物体。
Layer的z轴的位置则是通过anchorPoint来指定的,所谓的anchorPoint(锚点)就是在变换中保持不变的点,也就是某个Layer在变换中的原点,xyz三轴相交于此点。下图为锚点常用位置
在原点(0 , 0)沿着Y轴的正方向,得到如图坐标系, 首先在Z轴选择一个视点
添加两个child layer,观察区域便能看到两个child layer顶部的短线,绿色在前,红色在后,且长度相等
通过视点对顶部,作相对X轴的投影,得到视点投影
绿线、红线本来长度相等,通过视点投影后造成了“近大远小”的透视效果
所以只要在iOS垂直投影前,对layer作视点投影变换,就能得到透视效果
实践透视原理
使用上图的坐标系,红点为观察区域一点,对红点做视点投影,得到绿点,同时对红点做z轴的垂直线得到黑点。
使用相似三角形原理,得到如下公式
简化公式后,得到 方程1 ,绿点x轴的值只于视点z轴值有关
对红点做h = 1的齐次坐标(6, 0, 5, 1),通过乘以一个矩阵,得到变换后的绿点的齐次矩阵
变换后的矩阵只与视点z轴值有关,所以只设置m34,对(6, 0, 5, 1 + 5r)还原得到 方程2
结合 方程1 和 方程2 ,最后得到
至此只要修改变换矩阵m34的值为视点z轴值,便能得到相应的视点投影变换矩阵