Transform 实验室
矩阵变换:线性代数在计算机图形学中的应用
T()
矩阵乘法即变换的复合
在线性代数中,矩阵乘法不仅是数字的运算,更是 线性变换的复合。对向量 v 先做变换 A 再做变换 B,等价于做变换 BA:
B(A·v) = (BA)·v
这就是为什么 CSS transform 中 transform: rotate(45deg) scale(2) 和 transform: scale(2) rotate(45deg) 效果不同 -- 矩阵乘法不满足交换律。
2D 变换矩阵
使用齐次坐标,2D 变换用 3×3 矩阵表示:
平移 Translation
┌ 1 0 tx ┐
│ 0 1 ty │
└ 0 0 1 ┘
缩放 Scale
┌ sx 0 0 ┐
│ 0 sy 0 │
└ 0 0 1 ┘
旋转 Rotation
┌ cos(θ) -sin(θ) 0 ┐
│ sin(θ) cos(θ) 0 │
└ 0 0 1 ┘
剪切 Shear
┌ 1 shx 0 ┐
│ shy 1 0 │
└ 0 0 1 ┘
3D 变换与齐次坐标
3D 变换使用 4×4 矩阵。齐次坐标 (x, y, z, w) 中 w 分量使得透视投影可以用矩阵乘法表达:
┌ 1 0 0 0 ┐
│ 0 1 0 0 │
│ 0 0 1 0 │
└ 0 0 -1/d 1 ┘ // 透视投影矩阵
变换后 w' = 1 - z/d,除以 w' 实现透视除法,这就是透视投影的数学本质。
Canvas 实践:变换引擎
class Transform2D {
constructor() {
this.m = [1, 0, 0, 1, 0, 0];
}
translate(tx, ty) {
this.m[4] += this.m[0] * tx + this.m[2] * ty;
this.m[5] += this.m[1] * tx + this.m[3] * ty;
return this;
}
rotate(angle) {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
const [a, b, c, d] = this.m;
this.m[0] = a * cos + c * sin;
this.m[1] = b * cos + d * sin;
this.m[2] = c * cos - a * sin;
this.m[3] = d * cos - b * sin;
return this;
}
scale(sx, sy) {
this.m[0] *= sx;
this.m[1] *= sx;
this.m[2] *= sy;
this.m[3] *= sy;
return this;
}
apply(x, y) {
return {
x: this.m[0] * x + this.m[2] * y + this.m[4],
y: this.m[1] * x + this.m[3] * y + this.m[5]
};
}
}
矩阵分解
任意 2D 仿射矩阵 [a, b, c, d, e, f] 可以分解为:
M = T(e, f) × R(θ) × Scale(sx, sy) × Shear(shx)
这个分解在动画插值中极为重要 -- 直接对矩阵元素做线性插值会导致变形,而对分解后的参数做插值则平滑自然。
线性代数不是抽象的符号游戏,它是图形学的语言。每一个旋转、缩放、投影,都是矩阵的一次乘法。