很多 Unity 程序员第一次碰矩阵,往往是在 Shader、相机或者 Matrix4x4.TRS 这里。
API 能背,代码也能抄,但只要一遇到“为什么乘法顺序一变结果就错了”“为什么局部坐标和世界坐标对不上”,脑子就会乱。
根因通常不是不会写代码,而是前面的基础概念没真正连起来。
我自己最早也是先在项目里撞墙,后面才倒回来看这些基础概念。尤其是做挂点、技能范围和 UI 跟随时,只要点、向量、坐标系三件事没分开,后面查问题会非常慢。
这篇先不急着上 4x4,也不急着讲公式推导,先把最关键的三件事捋顺:点、向量、坐标系。
一、矩阵本质上是在描述“坐标如何变化”
如果只记一句话,我建议记这个:
矩阵不是一张表,它是一个“变换规则”。
同一个点,用不同坐标系描述,会得到不同数字;同一个向量,在旋转、缩放之后,也会得到新的数字。矩阵做的事情,就是把“旧坐标”稳定地映射成“新坐标”。
在图形学和游戏开发里,这件事太常见了:
- 把模型局部顶点变到世界空间
- 把世界坐标变到相机空间
- 把一个方向从角色本地前方变成世界中的真实朝向
- 把 UI 点位从屏幕空间转换到世界空间
所以,矩阵不是数学课里的额外负担,它其实就是坐标空间之间的翻译器。
二、先分清点和向量
这两个词在代码里经常混着用,但在矩阵语境下必须分清。
1. 点是什么
点表示“位置”。
比如 (3, 2) 表示二维平面上的一个位置,(1, 5, -2) 表示三维空间里的一个位置。
点最重要的特征是:它依赖原点。
原点换了,同一个点的坐标数值就会变化。
2. 向量是什么
向量表示“方向和长度”。
比如“向右走 3 个单位,向上走 2 个单位”,这就是向量 (3, 2)。
向量不关心自己从哪里出发,它只关心偏移量本身。
这也是为什么在 Unity 里:
transform.position更像点transform.forward、transform.right、Vector3.up更像向量
当你理解“点是位置,向量是偏移”,后面再看 MultiplyPoint 和 MultiplyVector 的差别,就不会只靠死记硬背了。
三、坐标系决定了数字的含义
很多初学者会误以为一个坐标数字是绝对真实的。
其实不是。
坐标一定是相对于某个坐标系而言的。
例如角色脚下有个武器挂点,它的本地坐标可能是 (0.2, 1.1, 0)。这个数字只在角色自己的局部坐标系里有意义。一旦角色转身、缩放、移动,这个挂点在世界空间里的真实位置就会变化。
于是就有了一个最常见的需求:
把局部坐标转换成世界坐标。
这件事背后用的就是矩阵。
四、为什么 2x2 矩阵已经足够解释线性变换
先看二维情况最直观。
一个二维向量:
$$
\begin{bmatrix}
x \
y
\end{bmatrix}
$$
乘上一个二维矩阵:
$$
\begin{bmatrix}
a & b \
c & d
\end{bmatrix}
\begin{bmatrix}
x \
y
\end{bmatrix}
$$
得到的新结果,其实就是这个向量被“重新组合”之后的坐标。
更实用的理解方式是:
矩阵的每一列,代表原坐标系基向量变换后的去向。
也就是说,矩阵其实在回答两个问题:
- 原来的 x 轴现在指向哪里?
- 原来的 y 轴现在指向哪里?
只要这两个基向量确定了,整个平面里所有向量的新位置也就都确定了。
这就是为什么矩阵特别适合描述旋转、缩放、错切这种线性变换。
五、Unity 里最值得建立的一个直觉:列向量就是“新坐标轴”
这是我觉得最有用的一条工程直觉。
在 Unity 里看 localToWorldMatrix 时,不要把它当成一堆数字。你可以先把它理解成:
- 第一列:局部 x 轴在世界里变成了什么方向
- 第二列:局部 y 轴在世界里变成了什么方向
- 第三列:局部 z 轴在世界里变成了什么方向
- 第四列:物体原点在世界里的位置
一旦这么看,很多问题会突然变简单:
- 为什么物体旋转后
right/up/forward变了 - 为什么父节点缩放会影响子节点方向长度
- 为什么非等比缩放会让局部坐标系“看起来歪掉”
这些都不是引擎在“偷偷做魔法”,而是矩阵把局部基向量映射到了新空间。
六、线性变换为什么不包含平移
这个点在图形学里很重要。
严格来说,线性变换要求原点不动。
所以旋转、缩放、错切都属于线性变换,但平移不属于。
这也是为什么后面讲 Unity 的 TRS 矩阵时,会引出齐次坐标和 4x4 矩阵。因为我们需要一种办法,把“本来不属于线性变换”的平移也放进统一的矩阵框架里处理。
先记住结论就够了:
- 纯旋转、缩放、错切可以用线性变换理解
- 平移需要更进一步的表示方式
下一篇就接着讲这个过渡是怎么发生的。
七、学矩阵时,Unity 里最常见的三个落地点
如果你是做客户端开发,不一定天天手写矩阵,但下面这些地方一定绕不过去:
1. Transform 坐标转换
TransformPoint、InverseTransformPoint、TransformDirection 这些接口,本质上都是矩阵乘法的封装。
2. Shader 顶点变换
模型空间到世界空间、观察空间、裁剪空间的连续变换,背后就是 MVP 矩阵链。
3. 相机和 UI 坐标换算
从屏幕点击位置反推世界坐标,或者把 3D 点投影到 UI 上,本质上都是空间变换问题。
也就是说,学矩阵不是为了考试,而是为了以后调这些问题时知道自己到底在调什么。
八、现阶段不用死磕推导,先把这几个认知钉牢
我建议先记住下面 4 条:
- 矩阵描述的是变换规则,不只是数字表格。
- 点是位置,向量是偏移,两者在变换里不能混看。
- 坐标永远依赖坐标系,局部和世界只是两个不同描述方式。
- 矩阵的列,可以理解为变换后的基向量。
只要这 4 条稳了,后面再看旋转矩阵、4x4、TRS、MVP,理解速度会快很多。
九、一个很实用的练习方法
如果你现在就在做 Unity 项目,我建议你开一个空场景做这几个实验:
- 建一个父物体和一个子物体。
- 分别修改父物体的位移、旋转、缩放。
- 实时打印子物体的
localPosition、position、localToWorldMatrix。 - 对照 Scene 视图看
right/up/forward的变化。
如果你懒得自己搭太多对象,可以先用一段最小代码直接感受“点”和“方向”进入世界空间之后的区别:
1 | Vector3 localPoint = new Vector3(0f, 0f, 2f); |
这个例子很简单,但很适合拿来建立第一层直觉:
- 点会跟着物体位置一起走
- 方向会跟着朝向变化,但不是一个“有落点的位置”
只做一遍,你对“矩阵在改什么”会比看半天公式更有感觉。
十、总结
矩阵最难的地方,不是算,而是抽象。
但一旦你把它和 Unity 的空间变换、Transform、Shader 顶点流转对应起来,它就不再是纯理论,而是非常实用的工程工具。
这篇先打地基。
下一篇我会继续讲二维旋转、缩放、错切分别是怎么落到矩阵上的,以及为什么“矩阵乘法顺序”会直接决定结果对不对。