一、二维平移变换:从矩阵到代码
各位同学,今天我们来聊聊图形学里最基础、也最容易被忽视的一个变换——平移。
说实话,我刚学图形学那会儿,觉得平移太简单了。不就是把点挪个位置吗?x加个dx,y加个dy,完事。但后来在项目中踩了坑才发现,事情没那么简单。
1.1 平移的数学本质
先看一个点 P(x, y)。要把它平移到新位置 P'(x', y'),平移向量为 (tx, ty)。
公式很简单:
x' = x + tx
y' = y + ty
嗯,这就是二维平移的全部数学原理。但问题来了——
为什么我们不能直接用矩阵乘法表示平移?
你想想看,旋转、缩放都可以写成 2×2 矩阵乘法的形式:
[x'] [a b] [x]
[y'] = [c d] [y]
但平移是加法,不是乘法。这就导致了一个尴尬的局面:
- 旋转:矩阵乘法
- 缩放:矩阵乘法
- 平移:矩阵加法
操作不统一,组合变换时就非常麻烦。我记得第一次做复合变换时,代码写得又臭又长,就是因为平移没法和其他变换合并成一个矩阵。
1.2 齐次坐标:一个巧妙的 trick
那怎么办?
图形学的前辈们想了个办法——给二维点加一个维度。
把点 (x, y) 变成 (x, y, 1)。这就是齐次坐标。
为什么加个 1 就能解决问题?我们来看看:
[x'] [1 0 tx] [x]
[y'] = [0 1 ty] [y]
[1 ] [0 0 1 ] [1]
展开计算:
x' = 1·x + 0·y + tx·1 = x + tx
y' = 0·x + 1·y + ty·1 = y + ty
1 = 0·x + 0·y + 1·1 = 1
结果完全一样!但形式上变成了矩阵乘法。
核心思想:齐次坐标用增加一个维度的方法,把加法伪装成了乘法。这样所有变换都可以统一用矩阵乘法表示。
1.3 齐次坐标的几何意义
你可能会问:为什么是 1,不是 2 或 3?
其实齐次坐标 (x, y, w) 对应的是二维空间中的点 (x/w, y/w)。当 w=1 时,就是普通的点。当 w=0 时,表示无穷远处的点——也就是方向向量。
我个人习惯把 w 理解为「缩放因子」。w=1 是标准状态,w=0 是方向,w≠1 时可以通过归一化转回标准状态。
小技巧:判断一个向量是点还是方向,看 w 分量。w=1 是点,w=0 是方向。平移对方向向量没影响,因为方向没有位置概念。
1.4 代码实现:Python + NumPy
理论说完了,上代码。我用 NumPy 实现一个完整的二维平移变换。
import numpy as np
def translation_matrix(tx, ty):
"""
生成二维平移矩阵(齐次坐标形式)
tx: x方向平移量
ty: y方向平移量
"""
return np.array([
[1, 0, tx],
[0, 1, ty],
[0, 0, 1]
])
def apply_transform(points, matrix):
"""
对点集应用变换
points: 形状为 (N, 2) 的数组,每行是一个二维点
matrix: 3x3 变换矩阵
"""
# 转换为齐次坐标
ones = np.ones((points.shape[0], 1))
homo_points = np.hstack([points, ones]) # (N, 3)
# 应用变换
transformed = homo_points @ matrix.T # 注意矩阵乘法顺序
# 转回二维坐标
return transformed[:, :2]
# 示例:平移一个三角形
triangle = np.array([
[0, 0],
[1, 0],
[0.5, 1]
])
T = translation_matrix(2, 3)
triangle_translated = apply_transform(triangle, T)
print("原始三角形顶点:")
print(triangle)
print("\n平移后三角形顶点:")
print(triangle_translated)
运行结果:
原始三角形顶点:
[[0. 0. ]
[1. 0. ]
[0.5 1. ]]
平移后三角形顶点:
[[2. 3.]
[3. 3.]
[2.5 4.]]
每个点的 x 加了 2,y 加了 3,完美。
1.5 避坑指南
我曾经在项目里犯过一个低级错误——矩阵乘法的顺序搞反了。
NumPy 中,如果你用 matrix @ points,要求 matrix 的列数等于 points 的行数。但我们的 points 是 (N, 3),matrix 是 (3, 3),直接乘会报错。
正确的做法是 points @ matrix.T,或者把 points 转置成 (3, N) 再乘。
注意:NumPy 的 @ 运算符和 matmul 函数,对二维数组执行的是矩阵乘法。一定要搞清楚维度匹配关系,否则结果完全不对。
1.6 本章知识体系
下面这张图总结了二维平移变换的核心逻辑:
1.7 总结
二维平移变换看似简单,但引出了图形学中一个非常重要的概念——齐次坐标。
- 平移的数学本质:加法运算,无法用 2×2 矩阵表示
- 齐次坐标:给二维点加一个维度 w,w=1 表示点,w=0 表示方向
- 平移矩阵:3×3 矩阵,左上角是单位矩阵,右上角是平移量
- 代码实现:用 NumPy 操作 3×3 矩阵,注意乘法顺序
说实话,齐次坐标这个概念我当初学的时候也觉得有点绕。但用多了就会发现,它真的是图形学里最优雅的设计之一。后面讲旋转、缩放、复合变换时,你会越来越体会到它的好处。
好,这一章就到这里。代码部分建议你亲自跑一遍,改改参数看看效果。实践出真知嘛。
课后练习:实现一个函数,输入一个矩形(四个顶点)和平移向量,输出平移后的矩形坐标。试试用循环和向量化两种方式实现,感受一下 NumPy 向量化的威力。