前两篇把线性变换讲到了旋转、缩放、错切,但还差一个最常用的动作没统一进来:平移。
这也是我觉得很多 Unity 初学者第一次看 Matrix4x4 会突然断层的原因。前面 2x2 还在讲“轴怎么变”,一到 4x4 就像突然多出一堆和项目代码很近、但又说不清为什么存在的数据。
这件事如果只站在线性代数的严格定义上看,会比较尴尬。
因为线性变换要求原点不动,而平移天生会把原点搬走。
可图形学又必须把旋转、缩放、平移放进一套统一流程,否则渲染管线和引擎实现会变得很碎。
解决方案就是齐次坐标。
一、为什么二维里要从 2x2 升到 3x3
二维点 (x, y),如果只用 2x2 矩阵,平移做不到。
因为 2x2 乘法本质上只能做线性组合,没法凭空加一个常量位移项。
比如你想把点变成:
$$
(x’, y’) = (x + t_x, y + t_y)
$$
这件事不是纯粹依赖 x 和 y 的线性组合,它额外需要一个常量“1”。
所以图形学里把二维点扩展成:
$$
\begin{bmatrix}
x \
y \
1
\end{bmatrix}
$$
于是平移就能写进 3x3 矩阵:
$$
\begin{bmatrix}
1 & 0 & t_x \
0 & 1 & t_y \
0 & 0 & 1
\end{bmatrix}
$$
这样一乘,位移项就自然加进去了。
二、这个“多出来的 1”到底有什么用
它的作用不是为了数学形式好看,而是给平移留了一个入口。
你可以把它理解成:
前两维还是原来的空间坐标,最后那一维负责把“仿射变换”里的常量项也纳入矩阵乘法。
有了这个常量维度之后,旋转、缩放、平移都能统一用矩阵连乘来描述。
这对引擎非常重要,因为:
- 数据结构统一
- 乘法链统一
- GPU 顶点处理统一
三、三维世界为什么会变成 4x4
同理,三维空间里的点 (x, y, z) 在图形学中通常扩展成:
$$
\begin{bmatrix}
x \
y \
z \
1
\end{bmatrix}
$$
于是三维里的平移、旋转、缩放,都能用 4x4 矩阵来表示。
这就是 Unity 里 Matrix4x4 的来源。
所以不要把 4x4 看成“为了复杂而复杂”,它的本质只是:
三维空间 + 一维齐次坐标 = 统一表达所有常见空间变换。
四、点和向量在齐次坐标里要区别对待
这个地方特别关键。
点通常写成:
$$
(x, y, z, 1)
$$
向量通常写成:
$$
(x, y, z, 0)
$$
为什么?
因为向量表示的是方向和偏移,它不应该受平移影响。
举个很实在的 Unity 对应:
- 一个顶点位置,应该跟着平移一起动
- 一个朝向向量,不应该因为物体整体平移就改变方向
这也正是 MultiplyPoint 和 MultiplyVector 行为不同的根本原因。
五、Unity 里怎么理解 4x4 矩阵的每一部分
如果把 Matrix4x4 当成 16 个数字,阅读成本很高。
更工程化的理解方式是:
- 前三列:局部坐标系三个基向量在目标空间中的样子
- 最后一列:原点平移到目标空间后的坐标
所以一个 localToWorldMatrix 可以同时回答两件事:
- 这个物体的轴朝哪儿了
- 这个物体被搬到哪儿了
旋转、缩放影响前三列。
平移主要影响最后一列。
六、为什么 TRS 能被合成一个矩阵
TRS 就是 Translation、Rotation、Scale。
单看它们像是三种不同操作,但因为都能写成 4x4 矩阵,所以可以连乘合成一个总矩阵。
也就是说:
$$
M = T \cdot R \cdot S
$$
或者在具体实现里是别的顺序,但本质上都是:
先准备好几段变换,再合成一个总规则。
这个总规则可以一次性把局部点变换到目标空间。
七、为什么引擎这么喜欢“统一成矩阵”
因为统一之后,很多流程都简单了:
- 父子节点变换可以直接矩阵相乘
- CPU 侧和 GPU 侧的顶点变换可以共享同一套表达
- 相机、物体、投影都能接在一个矩阵链里
从实现角度看,这是一种极高性价比的抽象。
八、一个很容易踩的坑:把点和方向都拿去做同一种乘法
很多自定义工具、程序化生成代码里,会出现这种 bug:
本来要转换一个方向,却用了点变换;或者本来要转换一个位置,却当成向量去乘。
后果通常是:
- 方向多了一段莫名其妙的偏移
- 位置少了平移项
- 射线起点和方向都不对
所以只要你遇到“坐标差一点但总不对”的问题,第一件事就该回头检查:
我现在处理的,到底是点还是向量?
九、在 Unity 里最直接对应的几个 API
这篇学完之后,再看这些接口会非常顺:
transform.localToWorldMatrixtransform.worldToLocalMatrixMatrix4x4.TRSmatrix.MultiplyPointmatrix.MultiplyVectormatrix.MultiplyPoint3x4
这些都不是孤立 API,而是在共享同一套齐次坐标和仿射变换框架。
如果你想把“点吃平移、向量不吃平移”这件事一次看明白,可以直接跑下面这段:
1 | Matrix4x4 matrix = Matrix4x4.TRS( |
第一行结果会整体往 x 正方向多出一段平移,第二行不会。项目里只要把这两者混了,很多“怎么总差一点”的 bug 就会出现。
十、总结
齐次坐标不是“数学家为了炫技加的一维”。
它解决的是图形学里一个非常现实的问题:怎么把平移和其他变换统一成一套矩阵乘法流程。
理解了这一点,后面再看 Unity 的 Matrix4x4,你会更容易看出每一块数据的含义。
下一篇开始,就正式落回到 Unity 的 Transform,把局部空间和世界空间的关系讲透。