Appearance
转置矩阵与矩阵属性
转置是矩阵的另一个重要运算,在法线变换和正交矩阵中有重要应用。
转置矩阵(Transpose)
矩阵的转置是将行和列互换:
$$ (\mathbf{M}^T){ij} = \mathbf{M} $$
记作 $\mathbf{M}^T$(读作"M 的转置")。
例如:
$$ \mathbf{M} = \begin{bmatrix} 1 & 2 & 3 \ 4 & 5 & 6 \end{bmatrix} \Rightarrow \mathbf{M}^T = \begin{bmatrix} 1 & 4 \ 2 & 5 \ 3 & 6 \end{bmatrix} $$
第一行变成第一列,第二行变成第二列。
对于4×4矩阵:
$$ \mathbf{M} = \begin{bmatrix} m_{11} & m_{12} & m_{13} & m_{14} \ m_{21} & m_{22} & m_{23} & m_{24} \ m_{31} & m_{32} & m_{33} & m_{34} \ m_{41} & m_{42} & m_{43} & m_{44} \end{bmatrix} $$
$$ \mathbf{M}^T = \begin{bmatrix} m_{11} & m_{21} & m_{31} & m_{41} \ m_{12} & m_{22} & m_{32} & m_{42} \ m_{13} & m_{23} & m_{33} & m_{43} \ m_{14} & m_{24} & m_{34} & m_{44} \end{bmatrix} $$
代码实现
javascript
class Matrix4 {
// ... 前面的代码 ...
transpose() {
const result = new Matrix4();
const e = this.elements;
const r = result.elements;
for (let row = 0; row < 4; row++) {
for (let col = 0; col < 4; col++) {
r[row * 4 + col] = e[col * 4 + row];
}
}
return result;
}
}使用示例:
javascript
const m = new Matrix4();
m.set(0, 1, 5); // 第0行第1列设为5
const mt = m.transpose();
console.log(mt.get(1, 0)); // 5 - 变成第1行第0列转置的性质
性质 1:两次转置还原
$$ (\mathbf{M}^T)^T = \mathbf{M} $$
转置两次回到原矩阵。
性质 2:乘积的转置
$$ (\mathbf{A} \times \mathbf{B})^T = \mathbf{B}^T \times \mathbf{A}^T $$
注意顺序反转!
性质 3:逆矩阵的转置
$$ (\mathbf{M}^{-1})^T = (\mathbf{M}^T)^{-1} $$
对称矩阵
如果 $\mathbf{M}^T = \mathbf{M}$,则称为对称矩阵。
例如:
$$ \begin{bmatrix} 1 & 2 & 3 \ 2 & 4 & 5 \ 3 & 5 & 6 \end{bmatrix} $$
对称矩阵关于主对角线对称。
正交矩阵(Orthogonal Matrix)
正交矩阵是一种特殊的矩阵,满足:
$$ \mathbf{M}^T \times \mathbf{M} = \mathbf{M} \times \mathbf{M}^T = \mathbf{I} $$
也就是说:转置等于逆矩阵。
$$ \mathbf{M}^T = \mathbf{M}^{-1} $$
几何意义
正交矩阵表示旋转和镜像变换,不包含缩放。
正交矩阵的性质:
- 保持长度:$|\mathbf{M} \times \mathbf{v}| = |\mathbf{v}|$
- 保持角度:变换后的向量夹角不变
- 列向量互相垂直:且都是单位向量
为什么重要?
正交矩阵的逆矩阵非常容易计算:只需转置!
这在图形学中很有用,因为旋转矩阵都是正交矩阵。
代码实现
javascript
class Matrix4 {
// ... 前面的代码 ...
isOrthogonal() {
const mt = this.transpose();
const product = this.multiply(mt);
return product.isIdentity();
}
}应用:法线变换
法线(Normal)是垂直于表面的向量,在光照计算中至关重要。
问题:当我们用矩阵 $\mathbf{M}$ 变换一个物体时,如何变换法线?
错误做法:直接用 $\mathbf{M}$ 变换法线。
正确做法:使用 $(\mathbf{M}^{-1})^T$(逆矩阵的转置)。
为什么?
假设表面上有切向量 $\mathbf{t}$ 和法向量 $\mathbf{n}$,它们垂直:
$$ \mathbf{t} \cdot \mathbf{n} = 0 $$
变换后的切向量:$\mathbf{t}' = \mathbf{M} \times \mathbf{t}$
如果变换后的法向量是 $\mathbf{n}' = \mathbf{G} \times \mathbf{n}$,要保持垂直:
$$ \mathbf{t}' \cdot \mathbf{n}' = (\mathbf{M} \times \mathbf{t}) \cdot (\mathbf{G} \times \mathbf{n}) = 0 $$
通过数学推导(这里省略细节),可以证明:
$$ \mathbf{G} = (\mathbf{M}^{-1})^T $$
特殊情况:正交矩阵
如果 $\mathbf{M}$ 是正交矩阵(如纯旋转),那么:
$$ (\mathbf{M}^{-1})^T = (\mathbf{M}^T)^T = \mathbf{M} $$
也就是说,纯旋转变换的法线可以直接用原矩阵变换。
但如果包含缩放,就必须用逆转置矩阵。
代码实现
javascript
function transformNormal(normal, modelMatrix) {
// 计算逆转置矩阵
const invTranspose = modelMatrix.invert().transpose();
// 变换法向量
return invTranspose.multiplyVector(normal).normalize();
}
// 使用示例
const normal = new Vector3(0, 1, 0);
const modelMatrix = new Matrix4();
// ... 设置变换矩阵 ...
const transformedNormal = transformNormal(normal, modelMatrix);小结
转置矩阵及相关属性:
- 转置:行列互换,$(\mathbf{M}^T){ij} = \mathbf{M}$
- 性质:$(\mathbf{A} \times \mathbf{B})^T = \mathbf{B}^T \times \mathbf{A}^T$(顺序反转)
- 对称矩阵:$\mathbf{M}^T = \mathbf{M}$
- 正交矩阵:$\mathbf{M}^T = \mathbf{M}^{-1}$,表示旋转和镜像
- 法线变换:使用 $(\mathbf{M}^{-1})^T$,而不是 $\mathbf{M}$
至此,我们完成了矩阵的基础运算。下一章将学习如何用矩阵实现具体的变换:平移、旋转、缩放。
练习:
- 手动计算一个3×3矩阵的转置
- 验证 $(\mathbf{A} \times \mathbf{B})^T = \mathbf{B}^T \times \mathbf{A}^T$
- 思考:为什么法线变换需要用逆转置矩阵?
- 实现
isOrthogonal()方法并测试