图形变换

在利用 MS 二开的时候,图形变换经常用到,下面对图形变换相关知识进行简要总结。如果想更加深入地学习,可以阅读《计算机图形学》

二维几何变换

基本变换

平移

将 P 移到 P' 点,代数表达为: \[ \begin{align} x'=x+t_x \\\\ y'=y+t_y \end{align} \] 转化成矩阵表达为: \[ \displaylines{ P'= P+T \\ P'= \begin{bmatrix} x' \\ y' \\ \end{bmatrix}, P= \begin{bmatrix} x \\ y \\ \end{bmatrix}, T= \begin{bmatrix} t_x \\ t_y \\ \end{bmatrix} } \]

旋转

将 (x,y) 绕 (0,0) 旋转 θ 角,代数表达为: \[ \begin{aligned} x'=xcos\theta-ysin\theta \\ y' =xsin\theta+ycos\theta \end{aligned} \]

通过极坐标来推导

转化为矩阵表达为: \[ \begin{align} P'=R \cdot P \\ R= \begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta \\ \end{bmatrix} \end{align} \]

对于绕任意点的旋转,可以将任意点先平移到原点,旋转之后,再反向平移,其矩阵表达式见齐次坐标章节。

缩放

绽放的代数表达为: \[ \begin{align} x'=S_x\cdot x \\ y'=S_y\cdot y \end{align} \] 转化为矩阵表达为: \[ \begin{align} P'=S \cdot P \\ S= \begin{bmatrix} S_x \\ S_y \\ \end{bmatrix} \end{align} \]

齐次坐标

从上面的基本变换中,我们可以看出,每个基本变换都可以表示为普通矩阵形式: \[ P' = M_1 \cdot P + M_2 \]

  • 对于平移

    \(M_1\) 为单位矩阵,\(M_2\) 为平移的参数

  • 对于旋转和缩放

    \(M_1\) 为以原点为基准的旋转缩放矩阵,\(M_2\) 为实际基准点的平移参数

所以,为了利用这个公式产生先缩放,再旋转,后平移这样的变换顺序,必须一步一步地计算变换的坐标,不仅不方便使用,且效率低下。为了解决这个问题,将 2x2 的矩阵扩充为 3x3 矩阵,从而将所有的变化组合成单一矩阵来表示。

齐次坐标

将二维坐标表示 (x,y) 扩充到三维表示 (\(x_w,y_w,w\)) ,称为齐次坐标 (homogeneous coordinate) ,齐次坐标 (homogeneous coordinates) 或投影坐标 (projective coordinates) 是指一个用于投影几何里的坐标系统,如同用于欧氏几何里的笛卡儿坐标一般。

齐次坐标的引入

欧式空间里,两条公面的平行线无法相交,但是在投影空间(Projective Space)里不是这样。一个直观的表示如下:两条轨道的间距随着视线变远而逐渐变小,直到在无限远处相交。

img

在欧式空间里采用\((x, y, z)\)表示一个三维点,但是无穷远点\((\infty, \infty, \infty)\)在欧式空间里是没有意义的,在投影空间中进行图形和几何运算并不是一个简单的问题,为了解决这个问题,数学家 August Ferdinand Möbius 提出了齐次坐标系,使用 N+1 个量来表示 N 维坐标。例如在二维齐次坐标系中,我们引入一个量w,将一个二维点\((x, y)\)重新表示为\((X, Y, w)\)的形式,其中转换关系为: \[ \begin{align} x = \frac{X}{w} \\ y = \frac{Y}{w} \end{align} \]

例如,欧式坐标中的一个二维点\((1, 2)\)可以在齐次坐标中表示为\((1, 2, 1)\),如果点逐渐移动向无穷远处,其欧式坐标变为\((\infty, \infty, \infty)\),齐次坐标变为\((1, 2, 0)\)。其中齐次坐标在表示无穷远处的点时不需要用到\(\infty\)

其中齐次坐标\((1, 2, 1)\)等价于齐次坐标\((2, 4, 2)\)…即\((k, 2k, k),k \in R\),此处这些点具有尺度不变性,是齐性的(结构相似的),所以称为齐次坐标

平行线相交的不太严格的证明

欧式空间中假设有如下两条平行线: \[ \begin{align} Ax + By + C = 0 \\ Ax + By + D = 0 \end{align} \] 上面两条先在欧式空间中除非 \(C = D\),否则不相交。使用 \(\frac{x}{w}, \frac{y}{w}\) 替换 \(x, y\) (正如前文提到的使用\(N+1\)个量表示 N 维坐标,这里增加了一个量 w),可以得到: \[ \begin{align} Ax + By + Cw = 0 \\ Ax + By + Dw = 0 \end{align} \] 上式可以得到解\((x, y, 0)\),即两条平行线的齐次坐标表示在\((x, y, 0)\)也就是无穷点处相遇

当然这只是一个不严格不严谨的表示,齐次坐标真正的作用在于下文。

齐次坐标可以区分点与向量

以二维空间为例,\((a, b)\) 这样的表示既可以是一个坐标表示,也可以是一个向量表示。假设这个坐标系 \(xOy\) 中两个基向量为 \(\vec{x}, \vec{y}\),坐标原点为 o,则其中

  • 表示向量 \(\vec{v}\) 时,代表 \(\vec{v} = a\vec{x} + b\vec{y}\)
  • 表示一个点 p 时,代表 $ p - o = a + b $

如果没有附加说明,我们不能区别 \((a, b)\) 表示的是向量还是点。用三个量来表示的话,我们可以明确的区分向量和点

  • 齐次点 \((a, b, 1)\)

    \(w=1\) 是为了方便与欧拉坐标进行对应

  • 齐次向量 \((a, b, 0)\)

    欧拉坐标系中,向量是一个射向无穷远的射线,所以用 \(w=0\) 来表示。

基于齐次坐标的矩阵表示

通过基本变换,可以很容易得到下列矩阵:

平移

矩阵表达: \[ \begin{bmatrix} x' \\ y' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \] 简写为: \[ P'=T_{(t_x,t_y)} \cdot P \]

逆平移变换为: \[ T_{(t_x,t_y)}^{-1} = \begin{bmatrix} 1 & 0 & -t_x \\ 0 & 1 & -t_y \\ 0 & 0 & 1 \end{bmatrix} \]

旋转

矩阵表达: \[ \begin{bmatrix} x' \\ y' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} cos \theta & -sin \theta & 0 \\ sin \theta & cos \theta & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \]

简写为: \[ P'=R_{(\theta)} \cdot P \]

逆旋转变换为: \[ R_{(\theta)}^{-1} = \begin{bmatrix} cos (-\theta) & -sin (-\theta) & 0 \\ sin (-\theta) & cos (-\theta) & 0 \\ 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} cos \theta & sin \theta & 0 \\ -sin \theta & cos \theta & 0 \\ 0 & 0 & 1 \end{bmatrix} \]

缩放

矩阵表达: \[ \begin{bmatrix} x' \\ y' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} S_x & 0 & 0 \\ 0 & S_y & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \] 简写为: \[ P'=S_{(S_x,S_y)} \cdot P \] 逆绽放矩阵为: \[ S_{(S_X,X_Y)}^{-1} = \begin{bmatrix} \frac{1}{S_x} & 0 & 0 \\ 0 & \frac{1}{S_y} & 0 \\ 0 & 0 & 1 \end{bmatrix} \]

反射(镜像)

关于 x 轴镜像

\[ \begin{bmatrix} x' \\ y' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \]

关于 y 轴镜像

\[ \begin{bmatrix} x' \\ y' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} -1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \]

绕任意轴镜像

可以先将任意轴旋转到 x 或 y 轴上,然后再应用上述镜像矩阵。

错切

相对于 x 轴的 x 方向错切

\[ \begin{bmatrix} x' \\ y' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} 1 & sh_x & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \]

该矩阵将坐标转换成: \[ x'=x+sh_x \cdot y \\ y'=y \]

相对于 y 轴的 y 方向错切

\[ \begin{bmatrix} x' \\ y' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 \\ sh_y & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \]

该矩阵将坐标转换成: \[ \begin{align} x'=x \\ y'=y+sh_y \cdot y \end{align} \]

逆变换

将经过矩阵变换的点再逆反到原来的位置,需要对点进行逆变换。逆变换其实就是对原矩阵求逆。 \[ \begin{align} P' &= M \cdot P \\ M^{-1} \cdot P' &= M ^{-1} \cdot M \cdot P \\ 所以: P &= M^{-1} \cdot P' \end{align} \] 怎么求逆矩阵呢?

一般通过初等变换法求取矩阵的逆,详见《代数》。

二维复合变换

利用矩阵表达式,可以将各个矩阵变换相乘,把任意的变换序列组合成一个复合的变换矩阵,提高计算效率。通过复合矩阵,可以实现图形的任意变化。

复合变换矩阵是从右向左变换的。

复合二维平移

\[ \begin{bmatrix} 1 & 0 & t_{x2} \\ 0 & 1 & t_{y2} \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} 1 & 0 & t_{x1} \\ 0 & 1 & t_{y1} \\ 0 & 0 & 1 \end{bmatrix} = \cdot \begin{bmatrix} 1 & 0 & t_{x1}+t_{x2} \\ 0 & 1 & t_{y1}+t_{y2} \\ 0 & 0 & 1 \end{bmatrix} \]

复合二维旋转

\[ R_{(\theta 2)} \cdot R_{(\theta 1)} = R_{(\theta1+\theta2)} \]

复合二维缩放

\[ \begin{bmatrix} S_{x2} & 0 & 0 \\ 0 & S_{y2} & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} S_{x1} & 0 & 0 \\ 0 & S_{y1} & 0 \\ 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} S_{x1} \cdot S_{x2} & 0 & 0 \\ 0 & S_{y1} \cdot S_{y2} & 0 \\ 0 & 0 & 1 \end{bmatrix} \]

通用二维基准点旋转

假设需要绕 \((x_r,y_r)\) 进行旋转,可以采用如下步骤实现:

  1. 平移对象使基准点位置移动到坐标原点
  2. 将对象绕坐标原点进行旋转
  3. 将对象平移回原位置

\[ \begin{bmatrix} 1 & 0 & x_r \\ 0 & 1 & y_r \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} cos \theta & -sin \theta & 0 \\ sin \theta & cos \theta & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} 1 & 0 & -x_r \\ 0 & 1 & -y_r \\ 0 & 0 & 1 \end{bmatrix} \]

通用二维基准点缩放

假设需要绕 \((x_s,y_s)\) 进行缩放,可以采用如下步骤实现:

  1. 平移对象使旋转点与坐标原点重合
  2. 将对象基于坐标原点进行缩放
  3. 将对象平移回原位置

\[ \begin{bmatrix} 1 & 0 & x_s \\ 0 & 1 & y_s \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} S_x & 0 & 0 \\ 0 & S_y & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} 1 & 0 & -x_s \\ 0 & 1 & -y_s \\ 0 & 0 & 1 \end{bmatrix} \]

通用二维定向缩放

  1. 先将定向缩放方向旋转至与 x 或 y 轴重合
  2. 利用参数 \(S_x\)\(S_y\) 进行缩放
  3. 最后将对象旋转回原位置

矩阵的理解

齐次矩阵各个分量的含义: $$ \[\begin{bmatrix} a_{11}(x方向的缩放) & a_{12}(x相对于y的变化量) & a_{13}(x的平移值) \\ a_{21}(y相对于x的变化量) & a_{22}(y方向的缩放) & a_{23}(y的平移值) \\ 0 & 0 & 1 \\ \end{bmatrix}\]

\[\begin{align} &--x 影响因子 \\ &--y影响因子 \\ &--齐次因子 \end{align}\] $$

复合矩阵复合顺序:

在进行矩阵的复合时,一定下一个矩阵左乘上一个矩阵,千万不能弄反了。在变换时,是从右向左变换的。

二维坐标系间的变换

在进行图形处理时,经常需要将对象从一个坐标系变换到另一个坐标系中,比如从对象的局部坐标系切换到世界坐标。

如上图所示,为了将 P 的 xy 坐标变换到 x'y' 坐标,必须将 x'y' 轴叠加到 xy 轴上,需要分两步进行:

  1. 将 x'y' 系统的坐标原点 \((x_0,y_0)\) 平移到 xy 系统的原点 \((0,0)\)
  2. 将 x' 轴旋转到 x 轴上(顺时针旋转 \(\theta\)

\[ \begin{bmatrix} const \theta & -sin(-\theta) & 0 \\ sin(-\theta) & const \theta & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} 1 & 0 & -x_0 \\ 0 & 1 & -y_0 \\ 0 & 0 & 1 \end{bmatrix} \]

三维几何变换

三维几何变换的方法是在二维的基础上扩充了 Z 坐标而得到的。

三维平移

\[ \begin{bmatrix} x' \\ y' \\ z' \\ 1 \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} \]

三维旋转

在三维空间中,图形对象可以绕任意轴进行旋转,但绕平行于坐标轴旋转是容易处理的。所以,我们通过适当的平移旋转,使得旋转轴与坐标轴对齐,然后再构建基于坐标轴的旋转矩阵,最后将上述矩阵复合成一个空间中的旋转矩阵。

如果沿着坐标轴正半轴观察原点时,绕坐标轴的正向旋转方向是逆时针方向。

绕 z 轴旋转

\[ \begin{bmatrix} x' \\ y' \\ z' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} cos \theta & -sin \theta & 0 & 0\\ sin \theta & cos \theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} \]

绕 x 轴旋转

将 x 替换成 y,y 替换成 z,z 替换成 x 即可得到。 \[ \begin{bmatrix} y' \\ z' \\ x' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} cos \theta & -sin \theta & 0 & 0\\ sin \theta & cos \theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} y \\ z \\ x \\ 1 \end{bmatrix} \]

标准形式: \[ \begin{bmatrix} x' \\ y' \\ z' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & cos \theta & -sin \theta & 0 \\ 0 & sin \theta & cos \theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \\ \end{bmatrix} \]

绕 y 轴旋转

在上式中,将 x 替换成 y,y 替换成 z,z 替换成 x 即可得到。 \[ \begin{bmatrix} z' \\ x' \\ y' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} cos \theta & -sin \theta & 0 & 0\\ sin \theta & cos \theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} z \\ x \\ y \\ 1 \end{bmatrix} \]

标准矩阵为: \[ \begin{bmatrix} x \\ y \\ z \\ 1 \\ \end{bmatrix} = \begin{bmatrix} cos \theta & 0 & sin \theta & 0\\ 0 & 1 & 0 & 0 \\ -sin \theta & 0 & cos \theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} \]

绕任意轴旋转

  1. 平移对象,使得旋转轴通过坐标原点
  2. 旋转对象使得旋转轴与某一坐标轴重合
  3. 绕该坐标轴完成指定的旋转
  4. 利用逆旋转使旋转轴回到其原始方向
  5. 利用逆平移使旋转轴回到其原始位置

三维缩放

缩放矩阵: \[ \begin{bmatrix} S_x & 0 & 0 & 0 \\ 0 & S_y & 0 & 0 \\ 0 & 0 & S_z & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \] 相对于任意点 \((x_s,y_s,z_s)\) 的缩放步骤:

  1. 平移给定点到原点
  2. 使用上述矩阵相对于坐标原点进行缩放
  3. 平移给定点回到原始位置

三维反射(镜像)

\[ \begin{bmatrix} R_x & 0 & 0 & 0 \\ 0 & R_y & 0 & 0 \\ 0 & 0 & R_z & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \]

反射矩阵是缩放矩阵的特例,即缩放因子小于0时,就变成了反射。

投影到平面

当某一个轴的缩放因子为0时,代表向另外两个轴形成的平面进行投影。比如下列矩阵代表向 \(z=t_z\) 平面投影。 \[ \begin{bmatrix} R_x & 0 & 0 & 0 \\ 0 & R_y & 0 & 0 \\ 0 & 0 & 0 & t_z \\ 0 & 0 & 0 & 1 \end{bmatrix} \] 对于绕任意平面的投影,可以将投影平面旋转到标准平面,然后再进行标准投影变换。

三维错切

三维错切与二维错切类似,不过前者较后者在每个方向上多一个影响变量。比如,影响 x 方向的错切有 y 和 z 两个方向。

三维观察

二维观察显示流程

  1. 使用建模坐标 (MC) 变换构造世界坐标 (WC) 系场景
  2. 将世界坐标转换为观察坐标 (VC)
  3. 将观察坐标 (VC) 转换为规范化设备坐标 (NC)
  4. 将规范化设备坐标 (NC) 映射到设备坐标 (DC)

三维观察显示流程

三维场景视图的计算机生成步骤有点类似于拍一张照片的过程。

  1. 安放相机,即在场景中确定一个观察位置
  2. 确定相机方向,即照相机朝哪个方向照及如果绕视线旋转照相机以确定相片的向上方向
  3. 按照相机的 “裁剪窗口”(镜头) 来修剪场景,让光线从可视表面投影到照相机的胶片上

三维观察实际流程:

  1. 建模变换
  2. 观察变换
  3. 投影变换
  4. 规范化变换和裁剪
  5. 视口变换

局部坐标

局部坐标系中的坐标称之为局部坐标,在使用中,我们经常需要获取局部坐标,那么如何获取呢?

这里再引用一下《飞出个未来》里的一句话:

引擎推动的不是飞船而是宇宙。飞船压根就没动过。

仔细想想,计算机中摄像机的原理也是相通的。如果想换个角度观察一座山,您可以移动摄像机也可以……移动山。后者在实际中不可行,但在计算机图形学中却十分方便。

所以,在计算机图形变换过程中,我们操作的永远是图形。

可以这样理解:局部坐标是将图形的局部坐标系通过平移、旋转、缩放等矩阵变换后,使得图形的局部坐标系与世界坐标系重合,这个时候,获得的世界坐标其它就是局部坐标。

此时,你可能会有疑问:在实际的三维软件里,定义了 ACS 后,图形相对于世界坐标系的位置一直没变,为什么会说变换的是图形呢?

这个就涉及到图形的变换与显示了,图形坐标系的变换公式为: \[ P' =M_{local}^{-1} \cdot M_{world} \cdot P \] \(P'\) 表示局部坐标,\(P\) 表示世界坐标,从上面的公式里可以看到,坐标的变化都是通过矩阵变换得到的,原坐标 \(P\) 一直没变。

局部坐标与世界坐标互转

在空间中,任意点的局部坐标系与世界坐标系的关系如下: \[ M_{local} \cdot P' = M_{world} \cdot P \] 通常情况下,世界坐标系 \(M_{world} = I\),即为单位矩阵。

所以,局部坐标可由正式求得: \[ P' = M_{local}^{-1} \cdot P \] 在实际的坐标变换中,如果想将世界坐标转换为局部坐标,我们可以:

  1. 构建局部坐标系矩阵 \(M_{local}\)
  2. 对局部坐标系求逆得 \(M_{local}^{-1}\)
  3. 用局部坐标系的逆矩阵左乘世界坐标系的矩阵

从上面可以看出,要将对象从坐标系 A 转换到坐标系 B,只需要在 A 的度量矩阵上左乘坐标系 B 的度量矩阵的逆变换即可。

即: \[ P_B = M_B^{-1} \cdot M_A \cdot P_A \]

如何构建坐标系的度量矩阵

构建某个坐标系的矩阵其原理是将世界坐标系矩阵(单位矩阵)进行平移旋转。

比如已知局部坐标系的原点为 \(P(x,y,z)\) 和三个基向量分别为\(V_1\), \(V_2\), \(V_3\),其度量矩阵为: \[ M = \begin{bmatrix} V_1.x & V_2.x & V_3.x & P.x \\ V_1.y & V_2.y & V_3.y & P.y \\ V_1.z & V_2.z & V_3.z & P.z \\ 0 & 0 & 0 & 1 \end{bmatrix} \]

从上面的公式中,可以推导得到世界坐标系的度量矩阵为: \[ M = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \]

扩充阅读

  1. 矩阵
  2. 矩阵的乘积/复合变换