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

[TOC]

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

基础知识

齐次坐标

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

矩阵的乘法

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

矩阵的线性变换

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

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

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

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

$$
D_2 = D_1 \cdot M =
\left[
\begin{matrix} a1 & b1 & c1 \ b2 & b2 & c2 \ a3 & b3 & c3 \end{matrix}
\right]
\left( \begin{matrix} z1 \ y2 \ z3 \end{matrix} \right)
= x1 \left( \begin{matrix} z1 \ y2 \ z3 \end{matrix} \right)

  • y2 \left( \begin{matrix} b1 \ b2 \ b3 \end{matrix} \right)
  • z3 \left( \begin{matrix} c1 \ c2 \ c3 \end{matrix} \right)
    = \left( \begin{matrix} X \ Y \ Z \end{matrix} \right)
    $$

坐标变换

平移

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

1
2
3
X = x + dx
Y = y + dy
Z = z + dz

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

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

缩放

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

1
2
3
X = x * Sx
Y = y * Sy
Z = z * Sz

同理,缩放矩阵为

旋转

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

1
2
3
X = X
Y = y*cosθ - y*sinθ
z = z*sinθ + z*cosθ

矩阵M为

绕Y轴旋转时

绕Z轴旋转时

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

Open3D示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np
import open3d as o3d
import copy

vec = np.identity(4)
vec[0][0] = 2
vec[1][1] = 2
vec[2][2] = 2
print(vec)

# filename = 'data/hello_smpl_stand_new.ply'
mbody = '/home/simon/tf_demo/vton3d/3dface/Align/data/hello_smpl_stand_new.ply'
mface = '/home/simon/tf_demo/vton3d/3dface/Align/data/mx_new.ply'

mmbody = o3d.io.read_triangle_mesh(mbody)

mesh_face = o3d.io.read_triangle_mesh(mface)
mmface = o3d.geometry.Geometry3D.transform(mesh_face, vec)
print(mmface)

o3d.visualization.draw_geometries([mmface, mmbody])

问题:给定两个坐标,如何计算变换矩阵?

webgl示例分析

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 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想创建一个平移矩阵, 可以使用下面的方式。

1
2
3
var translateMatrix = mat4.create(); //创建单位矩阵
mat4.translate(translateMatrix, translateMatrix, vec3.fromValues(dx, dy, dz));
复制代码

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

ref

人脸姿态估计(计算欧拉角)-CSDN博客