跳到主要内容

三维坐标变换原理-平移, 旋转, 缩放

· 阅读需 8 分钟

齐次坐标

给定一个二维点(x, y),那么形如(kx, ky, k)的所有三元组就都是等价的,它们就是这个点的齐次坐标(homogeneous)。齐次坐标就是将一个原本是 n 维的向量用一个 n+1 维向量来表示,是指一个用于投影几何里的坐标系统,如同用于欧氏几何里的笛卡儿坐标一般

矩阵的乘法

矩阵的乘法运算,阮一峰老师写的比较清楚,具体可以看 这里

矩阵的线性变换

矩阵的线性变换就是从一个线性空间 V1V_1 的某一个点跃迁到另一个线性空间 V2V_2 的另一个点的运动。也就是说是一个点不仅可以变换到同一个线性空间中的另一个点,而且可以变换到另一个线性空间中的另一个点去

矩阵和线性变换之间的关系: 矩阵本身描述了一个坐标系,矩阵与矩阵的乘法描述了一个运动。换句话说:如果矩阵仅仅自己出现,那么他描述了一个坐标系,如果他和另一个矩阵或向量同时出现,而且做乘法运算,那么它表示运动(线性变换)

数学表述为: b=Ma\vec b = M\vec a, 即矩阵 M 描述了向量 a\vec a 到向量 b\vec b 的运动

如将三维坐标 D1 经过矩阵 M 变换到坐标 D2, 就可以表达为:

D2=D1M=[a1b1c1a2b2c2a3b3c3](x1y2z3)=x1(a1a2a3)+y1(b1b2b3)+z1(c1c2c3)=(XYZ)D2 = D1·M=\begin{bmatrix}a1 & b1 & c1\\a2 & b2 & c2 \\a3 & b3 & c3 \\\end{bmatrix} \begin{pmatrix}x1\\y2\\z3\\\end{pmatrix}=x1\begin{pmatrix}a1\\a2\\a3\\\end{pmatrix} + y1\begin{pmatrix}b1\\b2\\b3\\\end{pmatrix} + z1\begin{pmatrix}c1\\c2\\c3\\\end{pmatrix} = \begin{pmatrix}X\\Y\\Z\\\end{pmatrix}

坐标变换

平移

假设在三维空间坐标系中, 点AiA_i(x, y, z)在 x 方向移动了 dx, y 方向移动 dy, z 方向移动了 dz。到达点AjA_j(X, Y, Z), 则

X = x + dx
Y = y + dy
Z = z + dz

如上所述, 则存在一个平移矩阵 M,使得AiM=AjA_iM = A_j,但是在纯粹的三维矩阵中,我们永远也找不到这样一个矩阵 M 使条件成立。此时可以借助齐次坐标。齐次坐标规定用一个 n+1 维度的向量来表示原来的 n 维向量. 此时将AiA_i(x, y, z) 表示为(x, y, z, 1), 则可以得到矩阵 M

M=[100001000010dxdydz1]M = \begin{bmatrix}1& 0 & 0& 0\\0 & 1 & 0 & 0\\ 0& 0 & 1& 0\\dx & dy & dz & 1\\\end{bmatrix}

验证: 假设AiA_i(4, 8, 2), x 方向移动了 dx, y 方向移动 dy, z 方向移动了 dz, 则AjA_j(4+dx, 8+dy , 2+dz)

Aj=AiM=(4821)[100001000010dxdydz1]=(4+dx8+dy2+dz1)A_j = A_i·M=\begin{pmatrix}4&8&2&1\\\end{pmatrix}\begin{bmatrix}1& 0 & 0& 0\\0 & 1 & 0 & 0\\ 0& 0 & 1& 0\\dx & dy & dz & 1\\\end{bmatrix} = \begin{pmatrix}4+dx& 8+dy & 2 + dz & 1\end{pmatrix}

缩放

假设在三维空间坐标系中, 点AiA_i(x, y, z)在 x 方向缩放了 Sx, y 方向缩放了 Sy, z 方向缩放了 Sz。到达点AjA_j(X, Y, Z), 则

X = x * Sx
Y = y * Sy
Z = z * Sz

同理,缩放矩阵为

M=[Sx0000Sy0000Sz00001]M = \begin{bmatrix}Sx& 0 & 0& 0\\0 & Sy & 0 & 0 \\ 0& 0 & Sz& 0\\0 & 0 & 0 & 1\\\end{bmatrix}

旋转

矩阵的旋转比较复杂,需要涉及到三角函数。 点AiA_i(x, y, z)绕 X 轴旋转 θ 度时, 到达点AjA_j(X, Y, Z), 则

X = X
Y = y*cosθ - y*sinθ
z = z*sinθ + z*cosθ

矩阵 M 为

M=[10000cosθsinθ00sinθcosθ00001]M = \begin{bmatrix}1& 0 & 0& 0\\0 & cosθ & sinθ & 0 \\ 0 & -sinθ& cosθ& 0\\0 & 0 & 0 & 1\\\end{bmatrix}

绕 Y 轴旋转时

M=[cosθ0sinθ00100sinθ0cosθ00001]M = \begin{bmatrix}cosθ& 0 & -sinθ& 0\\0 & 1 & 0 & 0 \\ sinθ & 0& cosθ& 0\\0 & 0 & 0 & 1\\\end{bmatrix}

绕 Z 轴旋转时

M=[cosθsinθ00sinθcosθ0000100001]M = \begin{bmatrix}cosθ& sinθ & 0& 0\\-sinθ & cosθ & 0 & 0 \\ 0 & 0& 1& 0\\0 & 0 & 0 & 1\\\end{bmatrix}

欧拉变换是绕 3 个旋转轴的旋转矩阵的乘积

示例分析

在 webgl 中, 在矩阵变换常用的库glmatrix中有计算平移矩阵的translate方法

/**
* Translate a mat4 by the given vector
*
* @param {mat4} out the receiving matrix
* @param {mat4} a the matrix to translate
* @param {vec3} v vector to translate by
* @returns {mat4} out
*/
function translate(out, a, v) {
var x = v[0],
y = v[1],
z = v[2]
var a00 = void 0,
a01 = void 0,
a02 = void 0,
a03 = void 0
var a10 = void 0,
a11 = void 0,
a12 = void 0,
a13 = void 0
var a20 = void 0,
a21 = void 0,
a22 = void 0,
a23 = void 0

if (a === out) {
out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]
out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]
out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]
out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]
} else {
a00 = a[0]
a01 = a[1]
a02 = a[2]
a03 = a[3]
a10 = a[4]
a11 = a[5]
a12 = a[6]
a13 = a[7]
a20 = a[8]
a21 = a[9]
a22 = a[10]
a23 = a[11]

out[0] = a00
out[1] = a01
out[2] = a02
out[3] = a03
out[4] = a10
out[5] = a11
out[6] = a12
out[7] = a13
out[8] = a20
out[9] = a21
out[10] = a22
out[11] = a23

out[12] = a00 * x + a10 * y + a20 * z + a[12]
out[13] = a01 * x + a11 * y + a21 * z + a[13]
out[14] = a02 * x + a12 * y + a22 * z + a[14]
out[15] = a03 * x + a13 * y + a23 * z + a[15]
}

return out
}

通常使用 translate 方法来创建一个平移矩阵, 之后再 shader 中便可以通过这个平移矩阵来计算 gl_Position 的值。 通过上面的结果我们知道平移矩阵由最后四位数决定, 所以只需要计算数组的最后四位数即可。 根据矩阵的运算法则, 即可得到结果。

通常如果在 webgl 想创建一个平移矩阵, 可以使用下面的方式。

var translateMatrix = mat4.create() //创建单位矩阵
mat4.translate(translateMatrix, translateMatrix, vec3.fromValues(dx, dy, dz))

得到平移矩阵后,传递到顶点 shader 中与需要计算的点相乘即可得到目标点的坐标。