线性变换

二维 线性变换 Linear Transformation 把输入坐标映射为

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

它必须保持原点,并满足可加性与齐次性,因此平移不属于线性变换。缩放、旋转、镜像和错切都可以直接写成 2×22\times2 矩阵。

二维变换

沿两条坐标轴分别缩放 sx,sys_x,s_y

S(sx,sy)=(sx00sy).S(s_x,s_y)= \begin{pmatrix}s_x&0\\0&s_y\end{pmatrix}.

绕原点逆时针旋转 θ\theta

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

这个矩阵可以从基向量的变化推出。旋转后的 xx 轴单位向量为

Rex=(cosθsinθ),R\mathbf e_x= \begin{pmatrix} \cos\theta\\ \sin\theta \end{pmatrix},

旋转后的 yy 轴单位向量与它垂直,并同样逆时针旋转:

Rey=(sinθcosθ).R\mathbf e_y= \begin{pmatrix} -\sin\theta\\ \cos\theta \end{pmatrix}.

线性映射的矩阵以各基向量的像为列,把两列并在一起就得到 R(θ)R(\theta)。这比单独记四个三角函数符号更容易检查。

关于 yy 轴镜像可写成 diag(1,1)\operatorname{diag}(-1,1)。水平错切的形式为

H(k)=(1k01),x=x+ky.H(k)= \begin{pmatrix}1&k\\0&1\end{pmatrix}, \qquad x'=x+ky.

旋转矩阵的逆等于转置,并且 R(θ)=R(θ)1R(-\theta)=R(\theta)^{-1}。二维缩放中,负系数的个数为奇数时会翻转坐标系朝向;两个系数都为负则相当于正缩放后再旋转 180180^\circ,整体朝向不变。任一系数为零都会压扁一个维度,使矩阵不可逆。

齐次坐标

平移 x=x+tx,y=y+tyx'=x+t_x, y'=y+t_y 含有常数项,无法由 2×22\times2 矩阵表示。齐次坐标 Homogeneous Coordinates 为二维坐标增加第三个分量:

点 (x,y)(x,y,1)T,向量 (x,y)(x,y,0)T.\text{点 }(x,y)\longrightarrow(x,y,1)^T, \qquad \text{向量 }(x,y)\longrightarrow(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}.

点的末分量为 1,所以会受到平移;向量表示两个点的差,末分量为 0,不应因坐标原点移动而改变。齐次坐标还解释了常见运算:点减点得到向量,点加向量得到点,而两个点相加没有直接的仿射几何意义。

w0w\ne0 时,(wx,wy,w)(wx,wy,w) 的任意非零整体倍数都表示同一个二维点 (x,y)(x,y)。这个比例等价性在透视投影中尤为重要。

更一般地,w0w\ne0 的齐次坐标 (X,Y,w)(X,Y,w) 对应仿射点 (X/w,Y/w)(X/w,Y/w)w=0w=0 时无法做这种除法,它表示无穷远方向。仿射变换的最后一行固定为 (0,0,1)(0,0,1),因此点仍是点、方向仍是方向;透视变换允许改变 ww,再通过齐次除法产生随深度变化的缩放。

仿射变换

线性变换与平移组合成 仿射变换 Affine Transformation

(xy1)=(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}.

仿射变换保持直线、平行关系和同一直线上的长度比,但不一定保持角度与绝对长度。使用齐次矩阵后,缩放、旋转、错切和平移都能参与同一种矩阵乘法,整条变换链可以预先合并。

变换次序

对列向量约定,若希望先应用 A1A_1,再应用 A2A_2,最终结果是

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

矩阵从右向左作用。先平移再旋转,与先旋转再平移得到的结果通常不同。例如让物体绕自身中心 c\mathbf c 旋转,应先把中心移到原点,再旋转,最后移回:

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

对列向量,代码中“先做”的变换写在矩阵乘积右侧;改变乘法顺序通常会改变旋转中心和最终位置。

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

把一串变换预乘为单个矩阵,可以显著减少大量顶点重复执行时的计算开销。

逆变换

逆变换 Inverse Transformation 用于从目标坐标系返回原坐标系。平移的逆是反向平移,缩放的逆是缩放倒数,旋转的逆是反向旋转。复合变换必须按相反次序逐个撤销:

(TRS)1=S1R1T1.(T R S)^{-1}=S^{-1}R^{-1}T^{-1}.

若某个缩放轴的系数为零,空间信息已经丢失,变换不可逆。工程中应避免在需要法线变换或坐标反查的路径里引入奇异矩阵。

三维变换

三维仿射变换使用 4×44\times4 齐次矩阵。点写成 (x,y,z,1)T(x,y,z,1)^T,向量写成 (x,y,z,0)T(x,y,z,0)^T。缩放和平移为

S=(sx0000sy0000sz00001),T=(100tx010ty001tz0001).S= \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= \begin{pmatrix} 1&0&0&t_x\\ 0&1&0&t_y\\ 0&0&1&t_z\\ 0&0&0&1 \end{pmatrix}.

x,y,zx,y,z 轴的旋转分别只改变另外两个坐标。任意三维旋转可以由三个轴旋转组合,但 欧拉角 Euler Angles 的次序会影响结果,并可能出现 万向节锁 Gimbal Lock。四元数能更稳定地插值旋转,不过不属于本课程的展开重点。

欧拉角按不同轴次序改变飞机姿态,并可能出现万向节锁

比较不同教材或 API 的矩阵前,应先统一列向量或行向量约定、左右手系和相机方向;否则即使矩阵形式相似,符号和乘法次序也可能不同。

按右手系和列向量约定,三个轴旋转矩阵为

Rx(θ)=(10000cosθsinθ00sinθcosθ00001),R_x(\theta)= \begin{pmatrix} 1&0&0&0\\ 0&\cos\theta&-\sin\theta&0\\ 0&\sin\theta&\cos\theta&0\\ 0&0&0&1 \end{pmatrix},

Ry(θ)=(cosθ0sinθ00100sinθ0cosθ00001),R_y(\theta)= \begin{pmatrix} \cos\theta&0&\sin\theta&0\\ 0&1&0&0\\ -\sin\theta&0&\cos\theta&0\\ 0&0&0&1 \end{pmatrix},

Rz(θ)=(cosθsinθ00sinθcosθ0000100001).R_z(\theta)= \begin{pmatrix} \cos\theta&-\sin\theta&0&0\\ \sin\theta&\cos\theta&0&0\\ 0&0&1&0\\ 0&0&0&1 \end{pmatrix}.

例如绕 xx 轴旋转时,xx 分量不变,yzyz 平面执行一次二维旋转。改变坐标系手性、向量位于矩阵哪一侧或把主动旋转改为坐标系旋转,都会改变符号与乘法次序。

法线变换

表面切向量会随物体的线性部分 MM 变换:

t=Mt.\mathbf t'=M\mathbf t.

法线的定义是与所有切向量垂直。若直接令 n=Mn\mathbf n'=M\mathbf n,非均匀缩放后一般有

(Mn)T(Mt)=nTMTMt0,(M\mathbf n)^T(M\mathbf t) = \mathbf n^TM^TM\mathbf t \ne0,

法线会失去垂直性。

设正确的法线变换为 n=Gn\mathbf n'=G\mathbf n。要求任意原切向量满足

(n)Tt=(Gn)TMt=nTGTMt=0.(\mathbf n')^T\mathbf t' = (G\mathbf n)^TM\mathbf t = \mathbf n^TG^TM\mathbf t =0.

原来已有 nTt=0\mathbf n^T\mathbf t=0,因此只要选择

GTM=I,G^TM=I,

就能保持这条关系。解得

G=MT.G=M^{-T}.

所以 法线矩阵 Normal Matrix 是模型或 Model–View 线性部分的逆转置:

n=normalize(MTn).\mathbf n' = \operatorname{normalize} \left( M^{-T}\mathbf n \right).

平移不影响方向,只取齐次矩阵左上角的 3×33\times3 线性部分。纯旋转时 MT=MM^{-T}=M,直接乘旋转矩阵没有问题;非均匀缩放和错切时必须使用逆转置。

归一化只能修正长度,不能修正非均匀缩放造成的方向错误。

任意轴旋转

令旋转轴 n\mathbf n 为单位向量。先把待旋转向量 p\mathbf p 分成平行和垂直两部分:

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

p=pp.\mathbf p_\perp =\mathbf p-\mathbf p_\parallel.

平行分量在旋转中保持不变。垂直平面内,p\mathbf p_\perpn×p\mathbf n\times\mathbf p_\perp 互相垂直、长度相同,构成二维旋转所需的两条基。因此

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

又因为 n×p=0\mathbf n\times\mathbf p_\parallel=\mathbf0,代入分解并整理:

p=cosαp+(1cosα)nnTp+sinα(n×p).\mathbf p' = \cos\alpha\,\mathbf p +(1-\cos\alpha) \mathbf n\mathbf n^T\mathbf p +\sin\alpha (\mathbf n\times\mathbf p).

把叉积写成反对称矩阵 [n]×p=n×p[\mathbf n]_\times\mathbf p=\mathbf n\times\mathbf p,便得到 Rodrigues 旋转公式 Rodrigues’ Rotation Formula

R(n,α)=cosαI+(1cosα)nnT+sinα(0nznynz0nxnynx0).R(\mathbf n,\alpha) =\cos\alpha I +(1-\cos\alpha)\mathbf n\mathbf n^T +\sin\alpha \begin{pmatrix} 0&-n_z&n_y\\ n_z&0&-n_x\\ -n_y&n_x&0 \end{pmatrix}.

前两项共同保留轴向分量并缩放垂直分量,第三项补上垂直平面内旋转 9090^\circ 的方向。代入 p=n\mathbf p=\mathbf n 可得 Rn=nR\mathbf n=\mathbf n;代入 α=0\alpha=0 可得 R=IR=I,都是直接的正确性检查。

若旋转轴经过点 c\mathbf c 而不经过原点,应先平移 c-\mathbf c,完成旋转后再移回:

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