线性变换
缩放矩阵 Scale Matrix
缩放矩阵使用对角阵.
x' 读作 "x prime".
-
x和y均匀缩放
x与y同时缩放 0.5 倍, s = 0.5 [x'] = [s, 0] [x] = [ 0.5x ] [y'] [0, s] [y] [ 0.5y ] -
x和y不均匀缩放
x缩放, y不变. sx = 0.5, sy = 1.0 [x'] = [sx, 0] [x] = [ 0.5x ] [y'] [0, sy] [y] [ y ]
翻转矩阵 Reflection Matrix
水平翻转
x相反, y不变
x' = -x
y' = y
矩阵形式:
[x'] = [-1, 0] [x] = [ -x ]
[y'] [ 0, 1] [y] [ y ]
切变矩阵 Shear Matrix
假设图形的 宽度为x, 高度为y, 切变长度为 a .
沿水平方向切变, 图像中所有点的y轴值均未发生变化, 所以有:
y' = y
当 y = 0 时, 在底边上的任意点在水平方向上均未发生移动, 移动距离为 0 .
当 y = 1 时, 在顶边上的任意点在水平方向上的移动距离均为 a .
当 y = 0.5 时, 在中间的任意点在水平方向上的移动距离均为 a/2 .
那么实际上任意点在水平方向上的移动距离为: a * y , 所以有:
[x'] = [1, a] [x] = [ x + a*y ]
[y'] [0, 1] [y] [ y ]
要想写出一个变换的要点是, 找出变化之前的 x,y 和 变化之后的 x',y' 之间的关系.
旋转矩阵 Rotate Matrix
默认情况下的旋转指的是围绕坐标系 原点(0,0) 进行的 逆时针方向 的旋转.
变换矩阵推导:
目的: 由 (x, y) => (x', y')
需要找出一个矩阵[?], 其与(x, y) 相乘得到(x', y')
[x'] = [A?, B?] [x]
[y'] [C?, D?] [y]
假设图形的边长为 1, 逆时针旋转的角度为 θ .
先由特殊点来思考如何变换, 例如用右下角与左上角推导:
1. 右下角的点为 (1, 0), 旋转后的点为 P(x', y').
右下角所在的边就是底边, 沿逆时针向上旋转.
那么已知边长为 1, 旋转角度为 θ,
底边 与 x轴投影 和 点P与x轴垂线, 三者构成直角三角形.
底边为斜边, x轴投影为 θ 的临边, 点P与x轴垂线为 θ 的对边:
cosθ = 临边 / 斜边 = x轴投影 / 底边 = p点的x / 1 = P_x'
sinθ = 对边 / 斜边 = 点P与x轴垂线 / 底边 = p点的y / 1 = P_y'
那么由右下角变换到点P为:
(1, 0) => (cosθ, sinθ)
矩阵表示:
[cosθ] = [A, B] [1]
[sinθ] [C, D] [0]
=>
cosθ = A*1 + B*0 = A
sinθ = C*1 + D*0 = C
=>
[x'] = [cosθ, B?] [x]
[y'] [sinθ, D?] [y]
2. 左上角的点为 (0, 1), 旋转后的点为 P(x', y').
左上角所在的边就是左边, 沿逆时针向左旋转.
那么已知边长为 1, 旋转角度为 θ,
左边 与 y轴投影 和 点P与y轴垂线, 三者构成直角三角形.
左边为斜边, y轴投影为 θ 的临边, 点P与y轴垂线为 θ 的对边:
cosθ = 临边 / 斜边 = y轴投影 / 左边 = p点的y / 1 = P_x'
-sinθ = 对边 / 斜边 = 点P与y轴垂线 / 左边 = p点的x / 1 = P_y'
注意这里移动后P点的x轴坐标是负值, 所以sinθ需要加一个负号.
那么由左上角变换到点P为:
(0, 1) => (-sinθ, cosθ)
矩阵表示:
[-sinθ] = [cosθ, B] [0]
[ cosθ] [sinθ, D] [1]
=>
-sinθ = cosθ*0 + B*1 = B
cosθ = sinθ*0 + D*1 = D
=>
[x'] = [cosθ, -sinθ] [x]
[y'] [sinθ, cosθ] [y]
变换矩阵为:
[cosθ, -sinθ]
[sinθ, cosθ]
线性变换
以上变换都可以统一为以下的表示形式, 称其为"线性变换":
变换后坐标 = 变换矩阵 ✖️ 变换前坐标
x' = ax + by
y' = cx + dy
[x'] = [a, b] [x]
[y'] [c, d] [y]
齐次坐标
为什么需要齐次坐标, 仿射变换
图形进行平移变换, 假设在x方向平移 tx, 在y方向平移 ty :
x' = x + tx
y' = y + ty
也就是说 x 和 y 需要加上某个常数.
那么是否能够像缩放, 旋转等变换一样使用矩阵表示?
按照之前的矩阵表示:
[x'] = [a, b] [x]
[y'] [c, d] [y]
发现这种形式并不能加上某个常数, 所以修改为:
[x'] = [a, b] [x] + [tx]
[y'] [c, d] [y] [ty]
可以看出平移不再符合线性变换的表示形式, 它是一个 "仿射变换".
人们为了让各种变换可以统一用一种形式表示, 后来发现引入齐次坐标可以解决这个问题.
2维空间的表示形式
给2维坐标增加一个维度:
2D点 = (x, y, 1)T
2D向量 = (x, y, 0)T
那么平移可以表示为一个 矩阵×向量 的形式:
[x'] = [1, 0, tx] [x] = [ x+tx ]
[y'] [0, 1, ty] [y] [ y+ty ]
[w'] [0, 0, 1] [1] [ 1 ]
那么为什么给2维的点或向量增加第3个维度是1或0, 它有哪些好处:
1. 向量 + 向量 = 向量 第3维 0+0=0
2. 点 - 点 = 向量 第3维 1-1=0
3. 点 + 向量 = 点 第3维 1+0=1
4. 点 + 点 = 中点 第3维 1+1=2
关于第4点为什么是中点?
当w≠0时, 令第3维度变为1
[x] => [x/w]
[y] [y/w]
[w] [ 1 ]
那么当 w=2 时, 各维度除以2, 得到的也就是中间点的坐标.
仿射变换的齐次坐标表示
1. 使用齐次坐标统一表示所有变换
仿射变换:
[x'] = [a, b] [x] + [tx]
[y'] [c, d] [y] [ty]
表示为齐次坐标:
[x'] = [a, b, tx] [x]
[y'] [c, d, ty] [y]
[1 ] = [0, 0, 1] [1]
那么这样也就是把平移变换这种特殊的变换也统一表示为了一个 矩阵×向量 的形式.
2. 各种变换的具体形式
规律总结:
[x'] = [a, b, tx] [x]
[y'] [c, d, ty] [y]
[1 ] = [0, 0, 1] [1]
1. 最后一行永远都是 0 0 1
2. 平移永远是写在最右列的前2个数
3. a,b,c,d 区域控制的是缩放和旋转
缩放
[sx, 0, 0]
[0, sy, 0]
[0, 0, 1]
旋转
[cosθ, -sinθ, 0]
[sinθ, cosθ, 0]
[0, 0, 1]
平移
[1, 0, tx]
[0, 1, ty]
[0, 0, 1 ]
变换组合
复杂变换可以通过一系列简单变换来完成.
变换的顺序是重要的, 比如先平移后旋转, 与先旋转后平移, 结果是不一样的.
因为矩阵的乘法不满足交换律.
例如先 旋转45度 再 平移(1,0) 的矩阵表示:
自右向左:
[x] = [1, 0, 1] [cos45°, -sin45°, 0] [x]
T(1,0)·R45 [y] [0, 1, 0] [sin45°, cos45°, 0] [y]
[1] [0, 0, 1] [ 0, 0, 1] [1]
假设有n个变换步骤, 则:
An(...A2(A1(X))) = An ... A2 · A1 · [x]
[y]
[1]
因为矩阵乘法满足结合律, 那么可以先计算A1~An部分,
而无论有多少个3X3齐次坐标矩阵相乘, 结果依旧是一个3X3矩阵.
那么也就是说一个3X3矩阵就可以表示非常复杂的组合变换.
变换分解
对于图形不在坐标原点情况下的变换, 可以通过先移动到原点, 再变换, 最后再移动回去.
例如, 对于任意点c进行旋转:
自右向左:
T(c) · R(θ) · T(-c)
顺时针旋转(逆旋转) 旋转矩阵的逆 = 旋转矩阵的转置
当角 θ 为负时, 图形按照顺时针旋转, 那么矩阵为:
Rθ = [cosθ, -sinθ]
[sinθ, cosθ]
=>
cosθ 与 cos-θ 结果一样, sin相反.
R-θ = [cosθ, sinθ]
[-sinθ, cosθ]
通过观察会发现:
实际上逆旋转的矩阵就是原旋转矩阵的转置, 行变列, 列边行.
而且逆旋转矩阵是原矩阵的逆矩阵.
重要性质: 在旋转变换中
旋转矩阵的逆 = 旋转矩阵的转置
在数学中, 如果一个矩阵的逆等于转置, 那么这个矩阵叫做 "正交矩阵".
3D 变换
3维空间的表示形式
3维空间的变换就是在2维的基础上再添加一个值:
2D点 = (x, y, 1)T
2D向量 = (x, y, 0)T
3D 点 = (x, y, z, 1)T
3D 向量 = (x, y, z, 0)T
当 w≠0 时, 有:
(x, y, z, w) 的3D点为 (x/w, y/w, z/w, 1)
3D空间的齐次坐标表示
[x'] = [a, b, c, tx] [x]
[y'] [d, e, f, ty] [y]
[z'] [g, h, i, tz] [z]
[1 ] [0, 0, 0, 1] [1]
在3维空间中也遵循, 先线性变换, 后计算平移量 的操作顺序.
缩放
[sx, 0, 0, 0]
[ 0, sy, 0, 0]
[ 0, 0, sz, 0]
[ 0, 0, 0, 1]
平移
[ 1, 0, 0, tx ]
[ 0, 1, 0, ty ]
[ 0, 0, 1, tz ]
[ 0, 0, 0, 1 ]
绕轴旋转 (x轴, y轴, z轴)
x轴:
x轴不变 (1,0,0)
[ 1, 0, 0, 0 ]
[ 0, cosθ, -sinθ, 0 ]
[ 0, sinθ, cosθ, 0 ]
[ 0, 0, 0, 1 ]
z轴:
z轴不变 (0,0,1)
[ cosθ, -sinθ, 0, 0 ]
[ sinθ, cosθ, 0, 0 ]
[ 0, 0, 1, 0 ]
[ 0, 0, 0, 1 ]
y轴:
y轴不变 (0,1,0)
[ cosθ, 0, sinθ, 0 ]
[ 0, 1, 0, 0 ]
[ -sinθ, 0, cosθ, 0 ]
[ 0, 0, 0, 1 ]
根据右手螺旋定则, 任意两轴叉乘得到第三轴:
x × y = z
y × z = x
z × x = y
由于 z x x 得到的y轴与 x × z 得到的y轴方向相反
所以y轴与其它两个轴的变换矩阵不同
任意方向旋转
任意的3D旋转都可以通过基本的绕轴旋转来组合完成.
Rxyz(α,β,γ) = Rx(α)Ry(β)Rz(γ)
将任意旋转分解为3轴基本旋转: 罗德里格斯旋转公式
旋转轴为(以原点为起点的向量n) n = (nx,ny,nz)T,旋转角度为 α
R(n,α) = cosαI + (1−cosα)nnT + sinα [ 0, -nz, ny]
[ nz, 0, -nx]
[-ny, nx, 0]
注意点:
- 必须有 (|n|=1)。如果 (n) 不是单位向量,先归一化。
- 这对应右手系、主动旋转(把向量转过去)。如果你在某些图形 API 里用的是被动旋转/坐标系旋转,可能会出现角度取负或矩阵转置的差异。
关于四元数
比如2维空间旋转15°的一个矩阵, 另一个是旋转25°的矩阵, 把这两个矩阵加起来求平均, 那么结果并不是旋转20° .
可以知道旋转矩阵并不适合直接做差值计算, 那么对于此类问题四元数就可以方便的解决.