计算机图形学数学基础译自 ScratchaPixel 网站,数学基础部分共八篇,本篇为第二篇,感兴趣的同学可以参考我的 GitBook 镜像。
矩阵简介 Intro to Matrices
矩阵(Matrix) 在图形处理方面扮演着极为重要的角色,我们将经常在3D程序中看到它的身影。
在之前的章节我们指出通过线性操作可以对点进行 平移(translation)或者 旋转(rotation)。例如我们可以通过增加坐标的值来移动一个点,或者使用 三角函数(trigonometric function)对一个向量进行旋转。现在(并不是矩阵定义),矩阵是一种可以集合上述所有转换(平移、旋转或伸缩)的结构。用点或向量乘以矩阵可以得到转换后的点或矩阵。集合所有转换的意思是指对于平移,旋转或伸缩可以组合在一起,如创建一个矩阵(1,1,1),先沿X轴旋转90度,再沿Z轴放大2倍(1,1,2),然后再平移(-2,3,1)。我们可以将一个点按这种顺序操作,不过需要较多的代码:
Vec3f translate(Vec3f P, Vec3f translateValue) {...}
Vec3f scale(Vec3f P, Vec3f scaleValue) {...}
Vec3f rotate(Vec3f P, Vec3f axis, float angle) {...}
...
Vec3f P = Vec3f(1,1,1);
Vec3f translateVal(-1, 2, 4);
Vec3f scaleVal(1,1,2);
Vec3f axis(1,0,0);
float angle = 90;
Vec3f Pt;
Pt = translate(P, translateVal); // translate P
Pt = scale(Pt, scaleVal); // then scale the result
Pt = rotateValue(Pt, axis, angle); // finally rotate the point
但如果是矩阵的话可以这样写:
Matrix4f M(...); // st the matrix for translation, rotation, scale
Vec3f P = Vec3f(1,1,1);
Vec3f Ptransformed = P * M; // do everything at once,translate, rotate, scale
将点P和矩阵M相乘可以得到与只对P处理相同的结果,这里主要展示矩阵在图形处理的应用以及使用矩阵的优点。
矩阵是什么 Matrices,What are they?
矩阵是什么?我们先从一个矩阵的例子说起,如果你阅读过计算机图形学的书,你会发现它们常常定义2维数组。为了定义一个2维数组,我们通常使用 m x n 这种形式。m和n分别代表矩阵的行和列。下列是一个3 x 5
的矩阵:
我们通常使用i,j表示矩阵中的特定位置的系数。
中,i表示行,j表示列。通常情况,在计算机图形学中,我们比较关注
3 x 3
和4 x 4
的 方阵(square matrices) ,下面是4 x 4
方阵在c++中的实现:
template<typename T>
class Matrix44 {
public:
Matrix44() {}
const T* operator[] (uint8_t i) const {return m[i];}
T* operator[] (unit*_t i) {return m[i];}
// initialize the coefficients of the matrix with the coefficients of the identity matrix
T m[4][4] = {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}};
};
typedef Matrix44<float> Matrix44f;
上述代码中有两行操作:
const T* operator [] (unit8_t i) const {return m[i];}
T* operator [] (unit8_t i) {return m[i];}
这些操作被称为 访问操作符(access operator) ,它们被用于通过提供明确的成员变量来访问矩阵的系数。在没有定义访问操作符时,我们需要:
Matrix44f mat;
mat.m[0][3] = 1.f;
在定义访问操作符后:
Matrix44f mat;
mat[0][3] = 1.f;
矩阵乘法 Matrix Multiplication
矩阵的乘法是点-矩阵或向量-矩阵转换的核心。矩阵的乘积是另一个矩阵:
之前的部分我们简单说过矩阵可以以一种简单的方式,处理点或向量的组合线性变换。它们是如何工作的,比较重要的一点是,我们需要理解,矩阵的乘法(matrix multiplication) 代表的是另外两个矩阵组合的结果。换句话说,对点或向量的组合变换需要执行的矩阵M1和M2,可以组合成一个矩阵M3。假设一个点由A到B需要利用矩阵M1,然后转换B到C需要利用矩阵M2,用M1乘以M2得到的M3可以直接完成从A到C的转换。需要指出的如果你有另外两个矩阵M4和M5,完成A到D到C的转换,用M4乘以M5同样会得到M3。
矩阵的乘法的成立有一条很重要规则:M1的列数必须等于M2的行数。换言之,两个相乘的矩阵必须是m x p
和 p x n
的形式:
下面来看如何得出新类、矩阵的系数,我们使用下标i和j表示行和列的索引。
表示矩阵M3中i行和j列的系数的值,假设
i=1
且j=2
(对应C++中M1的第2行和M2的第3列,因为数组的起始下标是0)。可以得到如下公式:
在C++中的表示:
Matrix44 operator * (const Matrix44& rhs) const {
Matrix44 mult;
for (unit8_t i = 0; i< 4; ++i) {
for (uint8_t j = 0; j< 4; ++j) {
mult[i][j] = m[i][0] * rhs[0][j] +
m[i][1] * rhs[1][j] +
m[i][2] * rhs[2][j] +
m[i][3] * rhs[3][j];
}
}
return mult;
}
需要指出的是矩阵的乘法不满足 交换率(commutative) ,M1 * M2
的结果和M2 * M1
的不同。