齐次坐标:为什么引入齐次坐标?
做图形学或者嵌入式UI开发的朋友,一定绕不开一个概念——齐次坐标。
我第一次接触它的时候,心里想的是:这不就是给坐标多加了一个1嘛,有啥了不起的?
后来真做项目了,才发现这个「多加的1」,简直是救命的操作。
为什么需要齐次坐标?
先说说痛点。
在二维平面上,我们通常用 (x, y) 表示一个点。平移、旋转、缩放,这些操作你肯定熟悉。
但问题来了:
- 旋转和缩放,可以用 2×2 矩阵搞定
- 平移呢?它是个加法操作,不是乘法
你想想看,如果我想把一堆点先旋转、再平移、再缩放,就得来回切换乘法和加法。代码写起来又乱又容易出错。
齐次坐标的核心目的:把平移也变成乘法。
说白了,就是让所有变换都能用矩阵乘法统一表达。
核心思想: 在二维坐标 (x, y) 后面加一个维度 w,变成 (x, y, w)。通常 w=1,表示一个点;w=0 表示一个方向向量。
齐次坐标的几何意义
这个「多加的1」到底代表什么?
我个人的理解是这样的:
普通坐标 (x, y) 是二维平面上的一个点。齐次坐标 (x, y, w) 其实是在三维空间里的一条射线。
怎么理解?
- 所有形如 (tx, ty, tw) 的点,都对应同一个二维点 (x/w, y/w)
- 当 w=1 时,就是最常用的表示
- 当 w=0 时,表示无穷远处的点——也就是方向
嗯,这里要注意:w=0 不能做除法,但它代表「方向」这个几何意义非常有用。
我在做嵌入式UI的矢量图形引擎时,就经常用 w=0 来表示光照方向或者法线方向。省去了单独处理方向向量的麻烦。
矩阵表示
有了齐次坐标,所有变换都变成了 3×3 矩阵乘法(二维情况)。
来看几个常用的变换矩阵:
平移矩阵
| 1 0 tx |
| 0 1 ty |
| 0 0 1 |
把点 (x, y, 1) 乘上去,得到 (x+tx, y+ty, 1)。完美。
旋转矩阵
| cosθ -sinθ 0 |
| sinθ cosθ 0 |
| 0 0 1 |
缩放矩阵
| sx 0 0 |
| 0 sy 0 |
| 0 0 1 |
复合变换
假设我想先旋转再平移再缩放,只需要把三个矩阵乘在一起:
M = M_scale * M_translate * M_rotate
然后对每个点做一次乘法就行。效率高,代码也干净。
我的习惯: 在嵌入式平台上,我会提前把复合矩阵算好,存成静态变量。运行时只做一次矩阵乘法,省掉大量浮点运算。
避坑指南
我曾经踩过的坑: 在STM32上做UI动画时,忘了把齐次坐标的 w 归一化。结果平移操作出现了奇怪的缩放效果。排查了半天才发现,是 w 从1变成了其他值。
所以每次做完变换,记得检查 w 是不是还是1。如果不是,就做一次归一化:x /= w, y /= w, w = 1。
知识体系总览
下面这张图,帮你理清齐次坐标的核心逻辑:
总结一下
齐次坐标不是什么高深的东西。它就是一个技巧——给坐标多加一个维度,让平移也能用矩阵乘法搞定。
- 为什么引入? 为了统一所有变换为矩阵乘法
- 几何意义? w=1 是点,w=0 是方向
- 矩阵表示? 3×3 矩阵,平移、旋转、缩放都能表达
我个人觉得,理解齐次坐标的关键就一句话:用升维的方式,解决降维的痛点。
下次你写UI动画或者图形变换时,试试用齐次坐标。你会发现代码简洁很多,bug也少很多。
一句话记住: 齐次坐标 = 普通坐标 + 一个1。这个1,让平移不再是异类。