进阶 #graphics#transform#math
2D/3D 变换与齐次坐标
齐次坐标用 n+1 维向量表示 n 维点——让平移、旋转、缩放都可用矩阵乘法表示。3D 图形中的 MVP 变换(模型-视图-投影)就是矩阵连乘
🎮 手机里”转动”一张照片——一个数学问题
你在手机上打开一张图片,用手指”旋转”它——这个过程看起来很简单。但计算机要精确计算:每个像素的新位置在哪?
这就是图形变换(Transformation)——用数学把图形从一种状态”变换”到另一种状态(平移、旋转、缩放)。
📐 变换的三种基本操作:
- 平移(Translation):移动位置
- 旋转(Rotation):转个角度
- 缩放(Scale):放大或缩小
# 2D 中的点 (x, y)
# 平移:(x+tx, y+ty)
# 旋转:(x·cosθ - y·sinθ, x·sinθ + y·cosθ)
# 缩放:(x·sx, y·sy)
🧮 齐次坐标——让”统一”成为可能
问题是:平移是”加法”(x+tx),但旋转和缩放是**“乘法”(矩阵乘向量)。能不能让所有变换都用一种运算**表示?
齐次坐标(Homogeneous Coordinates) 的解决方案:给每个 2D 点加一个维度(变成 3 维):
2D 点 (x, y) → 齐次坐标 (x, y, 1)
3D 点 (x, y, z) → 齐次坐标 (x, y, z, 1)
现在所有变换都可以表示为 3×3 矩阵的乘法了:
2D 变换矩阵
平移 (tx, ty): 缩放 (sx, sy):
[1 0 tx] [sx 0 0]
[0 1 ty] [0 sy 0]
[0 0 1 ] [0 0 1]
旋转 θ(绕原点):
[cosθ -sinθ 0]
[sinθ cosθ 0]
[0 0 1]
# 用齐次坐标做变换
# 平移 (3, 2)
# 点 (2, 1) → 齐次坐标 (2, 1, 1)
# 结果 = 矩阵 × 向量
import numpy as np
point = np.array([2, 1, 1])
T = np.array([[1, 0, 3],
[0, 1, 2],
[0, 0, 1]])
result = T @ point # (5, 3, 1) → 2D 点 (5, 3)
为什么齐次坐标这么重要?
多个变换可以合并成一个矩阵——先平移、再旋转、再缩放 = 一个矩阵:
# 多个变换可以"合并"成一个矩阵
M = T(3, 2) @ R(45°) @ S(2, 1)
# 每个顶点只需要一次矩阵乘法
vertex_transformed = M @ vertex
🏗️ MVP 变换——让 3D 世界出现在 2D 屏幕上
把 3D 场景渲染到 2D 屏幕,需要经过三步矩阵变换:
模型矩阵(Model) → 视图矩阵(View) → 投影矩阵(Projection)
本地坐标 世界坐标 相机坐标 裁剪坐标
↓ ↓ ↓ ↓
物体自身的坐标 物体在场景中的位置 从相机视角看 投影到屏幕上
① 模型矩阵(Model Matrix)
把物体从”本地坐标”放到”世界坐标”中:
# 一个立方体在原点(0,0,0)定义
# 模型矩阵把它移到 (10, 0, -5),旋转 45°,放大 2 倍
M = T(10, 0, -5) @ R(45°) @ S(2, 2, 2)
② 视图矩阵(View Matrix)
把世界坐标变成”从相机位置看到的样子”——相当于移动整个世界让相机回到原点。
③ 投影矩阵(Projection Matrix)
把 3D 坐标投影到 2D 屏幕上。两种投影:
透视投影: 近大远小(真实感,3D 游戏)
正交投影: 大小不变(工程制图、2D 游戏)
透视投影就像人眼看世界——近处的物体大,远处的物体小。
正交投影就像工程图纸——不管远近,尺寸都一样。
// 顶点着色器中——MVP 变换就是一行矩阵乘法
gl_Position = projection * view * model * vec4(aPos, 1.0);
📝 小结
| 概念 | 一句话 |
|---|---|
| 齐次坐标 | 多一维表示点——让平移也能用矩阵乘法 |
| 变换矩阵 | 平移、旋转、缩放——全部统一为矩阵乘法 |
| 矩阵合并 | T × R × S = 一个矩阵处理所有变换 |
| 模型矩阵 | 物体本地坐标 → 世界坐标 |
| 视图矩阵 | 世界坐标 → 相机视角 |
| 投影矩阵 | 3D → 2D(透视/正交) |
🎯 小练习:一个 2D 点 (2, 3),先绕原点旋转 90°,再沿 x 方向平移 5 个单位。写出变换矩阵,计算最终坐标。
为什么先学这个? 变换是图形学的数学基础。有了它,才能理解光栅化管线——GPU 怎么把 3D 模型变成屏幕上的像素。