变换的用途

几何变换把一个点、向量或物体从一个状态换算到另一个状态。相机沿轨迹移动、机器人关节弯曲、角色被压扁后弹回、图像缩放和旋转,都可以归结为坐标的系统变化。

几何变换 Geometric Transformation把坐标、位置或姿态换算到新的状态。动画中的关节运动通常先给出关节角,再通过一串局部变换得到末端位置;如果反过来给定手的目标位置,要求关节角如何变化,则属于逆运动学。三维场景成像也需要变换:模型先放入世界,视图再固定相机,投影最后把三维空间映射到二维屏幕。

变换需要满足三个工程要求:可以批量作用于顶点,可以和其他变换复合,也应当在条件允许时求出逆变换。矩阵正好提供了这三种操作的统一表示。

写出二维矩阵

二维线性变换可以写成

(x′y′)=(abcd)(xy).\begin{pmatrix}x'\\y'\end{pmatrix} = \begin{pmatrix}a&b\\c&d\end{pmatrix} \begin{pmatrix}x\\y\end{pmatrix}.

矩阵的每一列是相应基向量经过变换后的坐标。令 ex=(1,0)T\mathbf e_x=(1,0)^T、ey=(0,1)T\mathbf e_y=(0,1)^T,分别计算它们的变换结果,就能把两列直接写入矩阵。这比死记矩阵元素更可靠,也适用于三维变换。

矩阵的尺寸同时记录了它能作用于什么形状的数据。m×nm\times n 矩阵可以作用于 n×1n\times1 列向量,结果是 m×1m\times1;矩阵乘法的中间维度必须相同。把变换写成矩阵后,同一条规则只需存储一次,就能批量作用于网格的所有顶点。

缩放与反射

沿 x、y 两个方向分别缩放 sx,sys_x,s_y:

S(sx,sy)=(sx00sy),(x′y′)=S(sx,sy)(xy).S(s_x,s_y)= \begin{pmatrix}s_x&0\\0&s_y\end{pmatrix}, \qquad \begin{pmatrix}x'\\y'\end{pmatrix} =S(s_x,s_y)\begin{pmatrix}x\\y\end{pmatrix}.

当 sx=sys_x=s_y 时是均匀缩放,长度按统一比例变化,角度和形状比例保持不变;当两个系数不同时是非均匀缩放,物体会在一个方向拉长、在另一个方向压扁。动画中的 squash-and-stretch 就属于这一类变换。

系数为负时会发生反射。关于 y 轴的反射是

Ryreflect=(−1001),x′=−x,y′=y.R_y^{\mathrm{reflect}}= \begin{pmatrix}-1&0\\0&1\end{pmatrix}, \qquad x'=-x,\quad y'=y.

关于 x 轴的反射为 diag⁡(1,−1)\operatorname{diag}(1,-1)。线性部分的行列式可以判断面积缩放和定向:行列式为负时手性翻转,为零时至少有一个方向被压扁,变换不可逆。

两个负缩放系数的组合会翻转两次手性,几何上等价于一次正缩放再加上半周旋转;一个零系数则让某个方向的信息不可恢复。导入不同工具链的模型时,缩放符号、顶点绕序和法线朝向要一起检查。

错切

水平错切保持 y 坐标不变,让 x 的位移随高度线性变化。设顶边 y=1y=1 的水平位移为 aa,则

Hx(a)=(1a01),(x′y′)=(x+ayy).H_x(a)= \begin{pmatrix}1&a\\0&1\end{pmatrix}, \qquad \begin{pmatrix}x'\\y'\end{pmatrix} = \begin{pmatrix}x+ay\\y\end{pmatrix}.

推导只需观察三个位置:底边 y=0y=0 没有水平位移,顶边 y=1y=1 位移为 aa,中间高度的位移按比例过渡。因此位移是 ayay。错切保持面积,因为 det⁡Hx(a)=1\det H_x(a)=1,但会改变角度,把正方形变成平行四边形。

竖直错切为

Hy(a)=(10a1),y′=y+ax.H_y(a)= \begin{pmatrix}1&0\\a&1\end{pmatrix}, \qquad y'=y+ax.

旋转

没有额外说明时,二维旋转默认绕原点进行,正角度为逆时针方向。绕原点逆时针旋转 θ\theta:

R(θ)=(cos⁡θ−sin⁡θsin⁡θcos⁡θ).R(\theta)= \begin{pmatrix} \cos\theta&-\sin\theta\\ \sin\theta&\cos\theta \end{pmatrix}.

推导时先设矩阵为 (abcd)\begin{pmatrix}a&b\\c&d\end{pmatrix}。单位基向量 ex\mathbf e_x 旋转后变为 (cos⁡θ,sin⁡θ)T(\cos\theta,\sin\theta)^T,因此第一列确定为这两个数;ey\mathbf e_y 旋转后变为 (−sin⁡θ,cos⁡θ)T(-\sin\theta,\cos\theta)^T,于是第二列也确定。旋转不改变长度和角度,矩阵列向量自然构成标准正交基。

反向旋转只需把角度换成 −θ-\theta:

R(−θ)=R(θ)T=R(θ)−1.R(-\theta)=R(\theta)^T=R(\theta)^{-1}.

这条性质来自旋转矩阵的正交性,也说明求旋转的逆不需要通用矩阵求逆。

旋转保持任意向量的长度和夹角。代数上,若 RTR=IR^TR=I,则

∥Rx∥2=xTRTRx=∥x∥2,\lVert R\mathbf x\rVert^2 =\mathbf x^TR^TR\mathbf x =\lVert\mathbf x\rVert^2,

(Ra)⋅(Rb)=a⋅b.(R\mathbf a)\cdot(R\mathbf b)=\mathbf a\cdot\mathbf b.

这也是判断一个矩阵是否保持欧氏几何关系的重要依据。

先平移后旋转与先旋转后平移会得到不同的空间位置

图中两行使用相同的平移量和旋转角,但上行先平移再旋转,下行先旋转再平移。两种结果的朝向可以相同,中心位置却不同,直观显示了矩阵乘法不交换和变换顺序的几何后果。

线性变换

缩放、反射、错切和旋转都能写成 p′=Ap\mathbf p'=A\mathbf p,因此属于线性变换 Linear Transformation。线性映射满足

f(u+v)=f(u)+f(v),f(ku)=kf(u).f(\mathbf u+\mathbf v)=f(\mathbf u)+f(\mathbf v), \qquad f(k\mathbf u)=kf(\mathbf u).

它必然把原点映射到原点。二维矩阵乘法展开后只有 ax+byax+by 这样的齐次线性组合,无法产生固定常数项;平移要求 x′=x+txx'=x+t_x、y′=y+tyy'=y+t_y,所以平移不是二维方阵能直接表示的线性变换。

齐次坐标

为了让平移和其他变换使用同一种矩阵乘法,引入齐次坐标 Homogeneous Coordinates,把二维坐标增加一个分量:

点 (x,y)↦(x,y,1)T,向量 (x,y)↦(x,y,0)T.\text{点 }(x,y)\mapsto(x,y,1)^T, \qquad \text{向量 }(x,y)\mapsto(x,y,0)^T.

二维平移矩阵为

T(tx,ty)=(10tx01ty001).T(t_x,t_y)= \begin{pmatrix} 1&0&t_x\\ 0&1&t_y\\ 0&0&1 \end{pmatrix}.

它作用于点时得到

T(xy1)=(x+txy+ty1),T\begin{pmatrix}x\\y\\1\end{pmatrix} =\begin{pmatrix}x+t_x\\y+t_y\\1\end{pmatrix},

作用于向量时得到

T(xy0)=(xy0).T\begin{pmatrix}x\\y\\0\end{pmatrix} =\begin{pmatrix}x\\y\\0\end{pmatrix}.

向量表示方向和位移,整体平移不应改变它,所以末位取 0;点表示位置,平移必须生效,所以末位取 1。

齐次坐标还保留了点和向量的代数关系。两个点相减得到向量,点加向量得到点,两个向量相加仍是向量。更一般地,(X,Y,w)T(X,Y,w)^T 在 w≠0w\ne0 时对应欧氏点

(Xw,Yw).\left(\frac Xw,\frac Yw\right).

因此,同一个点可以由任意非零倍数的齐次向量表示;w=1w=1 是常用的规范形式,w=0w=0 表示无穷远方向。两个规范点相加后末位为 2,齐次除法把它还原为两点的中点。若使用带权代表元,则应先说明权重,不能把任意两个齐次向量直接相加都解释成中点。

仿射变换

线性变换与平移的组合称为仿射变换 Affine Transformation:

(x′y′1)=(abtxcdty001)(xy1).\begin{pmatrix}x'\\y'\\1\end{pmatrix} = \begin{pmatrix} a&b&t_x\\ c&d&t_y\\ 0&0&1 \end{pmatrix} \begin{pmatrix}x\\y\\1\end{pmatrix}.

左上角的 2×22\times2 区域是线性部分,最后一列是平移,最后一行 (0,0,1)(0,0,1) 是二维仿射矩阵的结构约束。矩阵作用在点上时先执行线性变换,再叠加平移。

仿射变换保持直线、平行关系以及同一直线上线段的比例。它不能产生透视投影中的近大远小,因为平行线在仿射变换下仍然平行。齐次坐标增加了一个分量和矩阵的一圈元素,换来的好处是平移、旋转、缩放可以统一复合。这里的“最后一行固定”只针对仿射变换;一般齐次投影矩阵的最后一行可以不同,齐次除法也会在那时真正改变欧氏坐标。

仿射变换复合后仍然是仿射变换。若先施加 (A1,t1)(A_1,\mathbf t_1),再施加 (A2,t2)(A_2,\mathbf t_2),则

A=A2A1,t=A2t1+t2.A=A_2A_1, \qquad \mathbf t=A_2\mathbf t_1+\mathbf t_2.

后一个线性部分会继续作用于前一个平移量,这正是矩阵顺序不能交换的具体原因。

复合与逆

列向量约定下,矩阵从右向左作用。若先施加 A1A_1,再施加 A2A_2,结果是

p′=A2A1p.\mathbf p'=A_2A_1\mathbf p.

先平移再旋转与先旋转再平移一般不同。前者会把平移向量一起旋转,后者的平移仍沿世界坐标的固定方向。矩阵乘法不满足交换律,所以变换顺序携带几何意义;结合律允许先把一长串共享的变换预乘成一个矩阵,再批量作用于大量顶点。

一个复合变换也可以拆成若干简单变换。若要让物体绕自身中心 c\mathbf c 旋转,依次执行:

  1. T(−c)T(-\mathbf c),把旋转中心移到原点;
  2. R(θ)R(\theta),绕原点旋转;
  3. T(c)T(\mathbf c),把物体移回去。

所以综合矩阵为

M=T(c)R(θ)T(−c).M=T(\mathbf c)R(\theta)T(-\mathbf c).

撤销变换要按相反顺序使用逆矩阵。基本变换的逆分别是反向平移、倒数缩放和反向旋转;若 M=TRSM=T R S,则

M−1=S−1R−1T−1.M^{-1}=S^{-1}R^{-1}T^{-1}.

当缩放系数为 0 时,空间信息已经丢失,矩阵不可逆。

对仿射矩阵

M=(At0T1),M=\begin{pmatrix}A&\mathbf t\\\mathbf0^T&1\end{pmatrix},

如果 AA 可逆,则

M−1=(A−1−A−1t0T1).M^{-1}=\begin{pmatrix} A^{-1}&-A^{-1}\mathbf t\\ \mathbf0^T&1 \end{pmatrix}.

这条公式把“反向且反序”写成了矩阵结构。实际数值计算中,旋转可以转置求逆;一般矩阵则通常用分解算法求解,避免显式求逆。

三维变换

三维点和向量的齐次坐标为

点 (x,y,z,1)T,向量 (x,y,z,0)T.\text{点 }(x,y,z,1)^T, \qquad \text{向量 }(x,y,z,0)^T.

三维仿射变换使用 4×44\times4 矩阵:

(x′y′z′1)=(abctxdeftyghitz0001)(xyz1).\begin{pmatrix} x'\\y'\\z'\\1 \end{pmatrix} = \begin{pmatrix} a&b&c&t_x\\ d&e&f&t_y\\ g&h&i&t_z\\ 0&0&0&1 \end{pmatrix} \begin{pmatrix} x\\y\\z\\1 \end{pmatrix}.

左上角 3×33\times3 矩阵 AA 负责线性变换,最后一列前三个数是平移量,最后一行是仿射结构。三维齐次代表元 (X,Y,Z,w)T(X,Y,Z,w)^T 在 w≠0w\ne0 时表示 (X/w,Y/w,Z/w)(X/w,Y/w,Z/w)。

三维缩放和平移矩阵为

S(sx,sy,sz)=(sx0000sy0000sz00001),T(tx,ty,tz)=(100tx010ty001tz0001).S(s_x,s_y,s_z)= \begin{pmatrix} s_x&0&0&0\\ 0&s_y&0&0\\ 0&0&s_z&0\\ 0&0&0&1 \end{pmatrix}, \qquad T(t_x,t_y,t_z)= \begin{pmatrix} 1&0&0&t_x\\ 0&1&0&t_y\\ 0&0&1&t_z\\ 0&0&0&1 \end{pmatrix}.

三维变换仍然遵守“线性部分先作用,平移后作用”的顺序。左上角的 3×33\times3 块可以表示缩放、错切、旋转或它们的组合;最后一列描述位置偏移,方向向量的齐次末位为 0,因此不会被平移改变。

主轴旋转

绕 x 轴旋转时 x 分量不变,y、z 在 yz 平面内做二维旋转:

Rx(α)=(10000cos⁡α−sin⁡α00sin⁡αcos⁡α00001).R_x(\alpha)= \begin{pmatrix} 1&0&0&0\\ 0&\cos\alpha&-\sin\alpha&0\\ 0&\sin\alpha&\cos\alpha&0\\ 0&0&0&1 \end{pmatrix}.

绕 z 轴旋转时 z 分量不变,x、y 使用二维旋转块:

Rz(γ)=(cos⁡γ−sin⁡γ00sin⁡γcos⁡γ0000100001).R_z(\gamma)= \begin{pmatrix} \cos\gamma&-\sin\gamma&0&0\\ \sin\gamma&\cos\gamma&0&0\\ 0&0&1&0\\ 0&0&0&1 \end{pmatrix}.

绕 y 轴旋转时,在垂直于它的平面中按 z,xz,x 排列坐标,可继续使用二维旋转模板:

Ry(β)=(cos⁡β0sin⁡β00100−sin⁡β0cos⁡β00001).R_y(\beta)= \begin{pmatrix} \cos\beta&0&\sin\beta&0\\ 0&1&0&0\\ -\sin\beta&0&\cos\beta&0\\ 0&0&0&1 \end{pmatrix}.

其中看似特殊的符号来自右手坐标轴的循环关系 x×y=zx\times y=z、y×z=xy\times z=x、z×x=yz\times x=y。例如

Ry(β)(0,0,1,0)T=(sin⁡β,0,cos⁡β,0)T,R_y(\beta)(0,0,1,0)^T=(\sin\beta,0,\cos\beta,0)^T,

说明正 z 轴向正 x 轴转;

Ry(β)(1,0,0,0)T=(cos⁡β,0,−sin⁡β,0)T.R_y(\beta)(1,0,0,0)^T=(\cos\beta,0,-\sin\beta,0)^T.

说明正 x 轴向负 z 轴转。这个符号不是笔误,换坐标手性时必须连同旋转正方向和叉积约定一起修改。

主轴旋转矩阵都满足正交性,行列式为 1,表示纯旋转不翻转定向。把二维旋转模板放到与旋转轴垂直的坐标平面中,就能重新得到三个矩阵;y 轴对应的平面采用 z,xz,x 的循环顺序,所以展开到通常的 x,y,zx,y,z 分量顺序时,两个正弦项的位置和符号与另外两轴不同。

绕 x 轴旋转时,垂直于轴的平面发生二维旋转

蓝色平面垂直于 x 轴,红色虚线箭头标出绕该轴的旋转。x 方向分量保持不变,y、z 分量在蓝色平面内按二维旋转规则变化;另外两条轴可用同样方法分析。

欧拉角

把三次主轴旋转按固定顺序复合,可以表示任意三维朝向,这组参数称为欧拉角 Euler Angles。例如飞机的俯仰 Pitch、偏航 Yaw 和滚转 Roll,分别描述抬头低头、左右转向和机身倾斜。以一种轴序为例:

Rxyz(α,β,γ)=Rx(α)Ry(β)Rz(γ),R_{xyz}(\alpha,\beta,\gamma) =R_x(\alpha)R_y(\beta)R_z(\gamma),

右侧的 RzR_z 最先作用。欧拉角有两个重要限制。第一,旋转顺序不能随意交换,不同轴序得到的朝向一般不同。第二,某些中间角取到奇异值时,第一和第三个旋转轴可能重合,参数失去独立性,这就是万向节锁。物体本身仍可以旋转,退化的是这组三个参数的表达方式。

万向节锁的后果是姿态到三个角的反解不再唯一,动画插值还可能出现某个方向难以独立控制。它不是说三维物体失去了旋转能力,而是欧拉角这组三参数在特定姿态失去了独立性。

欧拉角按不同轴次序改变飞机姿态

图中飞机的俯仰、偏航和滚转对应三条不同转轴;它们的轴序和“绕固定轴还是绕随物体运动的轴”会影响最终姿态。图示用于说明三个角的几何含义,奇异姿态需要从参数独立性单独分析。

任意轴旋转

欧拉角把旋转拆成三次主轴旋转;另一种表示直接给出单位旋转轴 n=(nx,ny,nz)T\mathbf n=(n_x,n_y,n_z)^T 和角度 α\alpha,称为罗德里格斯旋转公式 Rodrigues’ Rotation Formula。先讨论经过原点的轴。给定待旋转向量 p\mathbf p,按轴方向分解:

p∥=(n⋅p)n=nnTp,\mathbf p_\parallel=(\mathbf n\cdot\mathbf p)\mathbf n =\mathbf n\mathbf n^T\mathbf p,

p⊥=p−p∥.\mathbf p_\perp=\mathbf p-\mathbf p_\parallel.

平行分量沿旋转轴,旋转时保持不变。垂直平面中,n×p⊥\mathbf n\times\mathbf p_\perp 与 p⊥\mathbf p_\perp 垂直且长度相同,方向由右手定则确定。于是垂直分量旋转后为

p⊥′=cos⁡α p⊥+sin⁡α(n×p).\mathbf p_\perp' =\cos\alpha\,\mathbf p_\perp +\sin\alpha(\mathbf n\times\mathbf p).

把平行分量加回来:

p′=nnTp+cos⁡α(p−nnTp)+sin⁡α(n×p)=cos⁡α p+(1−cos⁡α)nnTp+sin⁡α(n×p).\begin{aligned} \mathbf p' &=\mathbf n\mathbf n^T\mathbf p +\cos\alpha(\mathbf p-\mathbf n\mathbf n^T\mathbf p) +\sin\alpha(\mathbf n\times\mathbf p)\\ &=\cos\alpha\,\mathbf p +(1-\cos\alpha)\mathbf n\mathbf n^T\mathbf p +\sin\alpha(\mathbf n\times\mathbf p). \end{aligned}

将叉积写成反对称矩阵

[n]×=(0−nznynz0−nx−nynx0),[\mathbf n]_\times= \begin{pmatrix} 0&-n_z&n_y\\ n_z&0&-n_x\\ -n_y&n_x&0 \end{pmatrix},

即可得到罗德里格斯旋转矩阵:

R(n,α)=cos⁡αI+(1−cos⁡α)nnT+sin⁡α[n]×.R(\mathbf n,\alpha) =\cos\alpha I +(1-\cos\alpha)\mathbf n\mathbf n^T +\sin\alpha[\mathbf n]_\times.

当 α=0\alpha=0 时它退化为单位矩阵;当 p=n\mathbf p=\mathbf n 时旋转轴自身保持不动;当 α=180∘\alpha=180^\circ 时

R(n,π)=2nnT−I,R(\mathbf n,\pi)=2\mathbf n\mathbf n^T-I,

只有垂直于轴的分量反向,轴向分量不变。若旋转轴经过空间点 c\mathbf c,先把轴移到原点,再旋转,最后移回去:

M=T(c)R(n,α)T(−c).M=T(\mathbf c)R(\mathbf n,\alpha)T(-\mathbf c).

绕指定点旋转的三个操作

图中的黑点是指定旋转点,未必是物体中心。先把黑点移到原点,再绕原点旋转,最后把黑点移回原位置;物体相对于黑点的距离保持不变。三幅操作对应矩阵从右向左的作用顺序。

旋转插值

旋转矩阵不适合直接逐元素线性插值。把 15∘15^\circ 和 25∘25^\circ 的两个旋转矩阵逐元素平均,结果通常不再正交,也不等于 20∘20^\circ 的旋转。动画和相机轨迹需要在两个姿态之间平滑插值,因此工程上常用单位四元数表示旋转。单位四元数的四个分量共同表示一个旋转,不能把它们分别理解成四个独立角度;它还要满足单位长度约束。

法线变换

位置和方向都可以用齐次坐标区分,但法线不能在所有情况下直接套用模型矩阵。法线的定义是:它必须与表面上每一个切向量垂直。若切向量经线性部分 MM 变换为

t′=Mt,\mathbf t'=M\mathbf t,

设法线的变换为 n′=Gn\mathbf n'=G\mathbf n,要求任意原始正交对都保持正交:

(Gn)T(Mt)=0.(G\mathbf n)^T(M\mathbf t)=0.

由于法线方向最终还会归一化,满足正交约束的一组方便选择是

GTM=I,G=M−T=(M−1)T.G^TM=I, \qquad G=M^{-T}=(M^{-1})^T.

因此正确的法线变换是

n′=normalize⁡(M−Tn).\mathbf n'=\operatorname{normalize}(M^{-T}\mathbf n).

非均匀缩放时,直接使用 MnM\mathbf n 会破坏法线与切向量的垂直关系。比如切向量 (1,1,0)(1,1,0)、法线 (1,−1,0)(1,-1,0),取 M=diag⁡(2,1,1)M=\operatorname{diag}(2,1,1),直接变换法线得到 (2,−1,0)(2,-1,0),与变换后切向量 (2,1,0)(2,1,0) 的点积为 3;逆转置得到 (1/2,−1,0)(1/2,-1,0),点积仍为 0。

实际渲染器通常把模型线性部分左上角 3×33\times3 的逆转置预先计算成 normalMatrix,再在着色器中逐顶点或逐片元使用。只有旋转和均匀缩放时,法线方向可以在普通变换后重新归一化;存在非均匀缩放或错切时,单纯归一化不能纠正方向偏差。若缩放系数为 0,曲面已经坍缩,法线和逆转置都失去定义。