Skip to content

图形学核心数学:从向量矩阵几何变换到最小二乘与SVD

在计算机图形学、三维重建(如点云处理、ICP 算法)以及游戏引擎开发中,数学是构建整个虚拟世界的基石。从最基础的“光照怎么算”,到复杂的“如何把两张不同视角拍摄的扫描点云精准对齐”,背后都有一套相通的数学逻辑。

本文将用直观的物理意义与推导,系统梳理图形学中最常用的数学计算,包括向量内积外积、矩阵变换顺序干扰原理、三维坐标管道、最小二乘平差以及基于 SVD 求解刚体配准的数学原理。其中,最小二乘与 SVD 部分采用"先直觉、后公式"的写法,方便零基础读者建立几何图像。


1. 向量几何基础:点乘与叉乘

1.1 点乘(内积 / Dot Product)

对于两个向量 ,它们的点乘定义为对应分量乘积之和,其运算结果是一个标量(即纯数值,没有方向)。 在三维空间中,其代数计算公式为各分量相乘后求和:

结合夹角 ,其几何意义公式为:

如果其中 是单位向量(),则点乘结果 恰好等于 方向上的投影长度(标量值)

核心性质与方向判定

基于余弦函数 的取值范围,点乘的符号可以直接用于判定两个向量的方向关系(这在图形学和游戏 AI 中极其常用):

  • :方向基本相同,夹角在 之间。
  • :正交(相互垂直),夹角为
  • :方向基本相反,夹角在 之间。

此外,点乘满足对称性)和线性)。

图形学核心应用:

  • Lambertian 漫反射光照:光照强度正比于光照方向单位向量 与面法线单位向量 的点乘 。点乘大于 0 表示光照照在正面,小于 0 表示光照在背面。
  • 背面剔除(Backface Culling):通过计算视线方向 与三角形面法线 的点乘。如果 ,说明该面朝向相机背面,可以直接丢弃不渲染。
  • 正交分解:将一个向量分解为平行于另一个向量的分量和垂直的分量。
  • 实战场景(视野锥检测):在游戏 AI 中,判断敌人是否在 NPC 的视野锥(FOV)内,只需计算 NPC 正前方向量 与敌人相对方向向量 的点乘。若 ,则在视野内,计算效率远高于反三角函数运算。
θba|a|·cos(θ)
图 1. 点乘投影几何意义

1.2 叉乘(外积 / Cross Product)

对于两个三维向量 ,它们的叉乘结果是一个同时垂直于 的新向量 ,这使得 构成一个右手系。

其几何意义为:

  1. 方向:由右手法则决定(右手四指从 弯曲向 ,大拇指指向即为叉乘方向)。
  2. 模长(面积),数值上等于以 为两边的平行四边形面积。

核心性质与三重纯量积

  • 反称性(Anti-symmetry)。交换叉乘顺序会使法线方向反转。
  • 正交性验证(三重纯量积):由于叉乘结果垂直于所在平面,因此它与原平面内任意向量的点乘恒为 0:

图形学核心应用:

  • 求三角形面法线:已知三角形三顶点 ,其法线可由边向量叉乘得到:,再进行归一化。
  • 判定点是否在三角形内部:已知三角形 和点 ,计算三个叉乘向量:。若这三个向量的方向(即 Z 分量符号)完全相同,说明点 在三角形内部。
  • 构建正交基 (LookAt 矩阵):给定相机位置、目标位置和世界向上方向 。通过(目标 - 相机)得到视线轴 ,然后 得到右向轴 ,最后 重新修正得到相机上向轴,从而构建相机的观察坐标系。
  • 实战场景(左右转向判定):在赛车游戏中,玩家车辆的朝向为 ,目标点方向为 。利用叉乘的上轴分量 可以判断目标点在车体的左侧还是右侧,从而决定方向盘是向左打还是向右打。
aba × b90°90°
图 2. 叉乘与右手法则几何意义

2. 仿射变换与矩阵:TRS 顺序与齐次坐标

2.1 齐次坐标的本质:为什么 3D 变换用 4x4 矩阵?

在三维空间中,旋转(Rotation)和缩放(Scaling)属于线性变换,可以完美表示为 的矩阵乘法:

然而,平移(Translation)的公式是:。这属于仿射变换,无法用 的矩阵相乘来表达(我们不能用乘法直接实现加法)。

为了让旋转、缩放和平移能够统一写成同一个矩阵乘法的形式,图形学引入了齐次坐标:将三维向量 扩展为四维

此时,平移变换可以写为:

齐次坐标的本质是:将三维空间中的仿射变换,提升到四维空间中,看作是一个线性剪切(Shear)变换,然后再投影回 的三维超平面上。


2.2 变换相乘的顺序陷阱

在图形学中,我们通常需要对一个物体同时进行缩放(S)、旋转(R)和平移(T)。将这些变换组合为一个矩阵 时,乘法的顺序至关重要

由于矩阵乘法不满足交换律(),变换的顺序会导致截然不同的结果。

标准顺序:TRS(从右向左读取)

在列主序下,标准组合变换矩阵为:

当这个矩阵作用于顶点 时,计算顺序是从右往左:

  1. 第一步(S):先在物体局部原点进行缩放
  2. 第二步(R):在原点进行旋转
  3. 第三步(T):最后平移到世界坐标系的目标位置。

顺序颠倒会发生什么?

  • 若先平移再旋转(RT)。 物体会先平移到某个位置,然后绕世界坐标原点进行旋转。这意味着物体不仅自己旋转,还会绕着原点“画圈圈”(类似月亮绕地球旋转),这通常不是我们想要的行为。
  • 若先平移再缩放(ST)。 物体的平移距离也会被按比例缩放。例如平移了 5 个单位,随后缩放 2 倍,物体会被发送到 10 个单位处。
  • 若先旋转再缩放(SR,非均匀缩放): 如果缩放是均匀的(XYZ 轴缩放相同),顺序不影响结果。但如果是非均匀缩放(比如只在 X 轴拉伸),先旋转再缩放会导致物体产生切变(Shear / 倾斜变形),完全破坏物体的原始形状几何结构。

2.3 绕任意锚点(Pivot Point)旋转的数学推导

默认的旋转矩阵均是绕坐标系原点 旋转。如果想让物体绕任意中心点 旋转,必须通过三步变换的组合:

  1. 将空间整体平移,使锚点 与原点重合:
  2. 绕原点进行标准旋转:
  3. 将空间平移回原处:

整个组合变换矩阵为:


2.4 前乘(Pre-Multiply)与后乘(Post-Multiply)

不同的图形 API 在矩阵内存排布和乘法定义上存在两套标准:

特性列主序(Column-Major)行主序(Row-Major)
典型代表OpenGL、GLSL、VTK(默认)DirectX、HLSL
数学写法
复合变换(作用顺序从右往左)(作用顺序从左往右)

核心铁律:向量写在哪边,就从哪边开始算

无论哪种体系,变换的"作用顺序"永远取决于矩阵离向量有多近——离向量最近的矩阵最先作用:

  • 列主序(向量写在右边 ):永远从右往左作用。
  • 行主序(向量写在左边 ):永远从左往右作用。

这是由数学表达本身决定的"物理定律",不会因为任何 API 调用而改变。

前乘/后乘只是决定"新矩阵插入到哪个位置"

当我们已有一个组合矩阵 ,需要追加一个新变换 时:

列主序下(从右往左作用于 ):

  • 前乘(PreMultiply) 放在 右侧。此时 更近,新变换先执行(在局部坐标系下操作)。
  • 后乘(PostMultiply) 放在 左侧。此时 更近,新变换最后执行(在世界坐标系下操作)。

行主序下(从左往右作用于 ),规律完全对称:

  • 放在 左侧 更近,新变换先执行
  • 放在 右侧 更近,新变换最后执行

一句话总结:列主序从右往左、行主序从左往右,这是不变的作用方向;前乘/后乘只是通过把新矩阵插入到队列的不同位置,来利用这个方向规律间接控制变换的先后顺序。

以 VTK 中的 vtkTransform 为例(列主序):

  • PreMultiply()(前乘模式,默认):新 Concatenate 的变换矩阵乘在已有矩阵的右侧 ()。新操作最先应用,相当于在物体的局部坐标系下进行变换。
  • PostMultiply()(后乘模式):新 Concatenate 的变换矩阵乘在已有矩阵的左侧 ()。新操作最后应用,相当于在世界全局坐标系下进行变换。

3. 三维坐标系统转换流程

顶点从美术建模的本地空间,最终呈现在屏幕像素上,需要经历经典的图形管线坐标空间转换:

  • 实战场景(3D 拾取与射线投射):在 RTS 游戏或 3D 建模软件中,鼠标点击屏幕其实是一个反向坐标转换过程。我们需要提取当前帧的屏幕坐标,通过逆视口变换和逆透视除法转换回裁剪空间,再依次乘以 ProjectionView 矩阵的逆矩阵(),最终生成一条世界空间中的三维射线(Ray),用于和场景中的物体包围盒做求交计算。

3.1 左右手坐标系转换

  • 右手坐标系(OpenGL/VTK):拇指为 X,食指为 Y,中指指向自己为 Z 轴正方向。
  • 左手坐标系(DirectX/Unity):中指指向屏幕里为 Z 轴正方向。

当把右手系的数据导入左手系引擎时,最直接的做法是翻转 Z 轴。对应的转换矩阵为:


4. 最小二乘平差与 SVD(奇异值分解)求解

在点云配准(ICP)或三维重建中,我们常常需要根据含噪的观测数据估计最优参数——比如"旋转多少、平移多少,两堆点才能对齐"。这属于**平差(Adjustment)**的范畴。其中,SVD 在求解刚体变换中扮演着核心角色。

本节先用生活化的直觉把整体流程讲清楚,再给出必要的公式。你可以先跳过公式,只看文字和流程图,也能理解算法在做什么。


4.1 最小二乘:让"误差平方和"最小

一句话理解

最小二乘就是:找一个最合适的参数,让所有误差的平方加起来最小。

想象你在做一个动画场景:有一堆点应该落在某条曲线上,但因为噪声,它们散开了。你想画一条"最合适的线",让所有点离这条线尽量近——这就是最小二乘。

它做的事情非常简单:

  1. 每个点都有一个误差(离预测位置有多远)
  2. 把所有误差的平方加起来,得到总误差
  3. 调整参数,让总误差最小
text
点1        点2          点3
 |          |            |
误差₁      误差₂        误差₃
 |          |            |
误差₁²  +  误差₂²  +  误差₃²  =  总误差  →  尽量小

为什么是平方? 因为平方让"大误差更痛"——算法会更努力减少大的偏差,而不是只微调已经对齐得不错的点。

为什么是和? 因为你要让所有点都尽量对齐,而不是只对齐某一个点。

与公式的对应关系

在更一般的情况下,我们有一组方程 (方程个数多于未知数,通常无精确解)。最小二乘要找使残差平方和最小的

求导并令其为零,就得到正规方程(Normal Equation)

你不需要记住推导细节。只要记住:正规方程就是"让误差平方和最小"在代数上的写法。


4.2 刚体配准:我们要找什么?

问题描述

已知两组一一对应的三维点集 ,我们要找一个旋转矩阵 和平移向量 ,使源点集 经过变换后尽量与目标点集 重合:

用生活化的比喻:你有两堆点,形状相同但姿态不同——比如两根方向不同的"香蕉",或同一只猫的两个不同 Pose。你要让源点云旋转 + 平移到目标点云的位置:

text
源点云(姿态 A)  →  旋转 R  →  平移 t  →  目标点云(姿态 B)

写成优化目标,就是最小二乘形式:

约束 的含义是: 必须是纯旋转,不能拉伸、不能镜像翻转。

这个算法叫 Kabsch 算法,是 ICP 等点云配准方法的核心步骤之一。


4.3 步骤一:质心对齐(去耦合)

直觉

想象你抓着两堆点的"中心",把它们都提到原点,让两个中心重合。做完这步之后:

  • 平移 暂时不用管(中心已经对齐了)
  • 只需要考虑旋转

这叫去耦合——把"旋转"和"平移"两个未知数拆开,先解旋转,再反推平移。

计算

先求两组点集的几何中心(质心):

把每个点减去各自质心,得到中心化坐标:

问题简化为只求旋转:

旋转求出后,平移可以直接算出来:

含义很直观:把源质心旋转过去,再平移到目标质心的位置。


4.4 步骤二:从"距离最小"到"方向对齐"

质心对齐后,问题变成:找一个旋转 ,让每个源点 尽量靠近对应的目标点

直觉:最大化方向重合

把单个点的误差平方展开后,会发现其中有两项是常数(点的长度不变),真正影响结果的是中间这一项:

因此,最小化总距离误差,等价于最大化所有点方向重合程度之和

几何上怎么理解? 是旋转后的源点。它与 做点乘,表示两个向量在方向上的重合程度——方向越一致,点乘越大。所以这一步的本质就是:让两组点云的方向"对得越齐越好"

跨协方差矩阵 H

把所有点的内积累加,可以写成矩阵形式:

其中 跨协方差矩阵,描述了两组点云在各坐标轴方向上的关联分布。后续 SVD 就是对这个矩阵做分解。


4.5 步骤三:SVD 找主方向并对齐

这是整个算法最关键、也最容易让人困惑的一步。先用直觉理解,再看公式。

一句话几何直观:印章变椭圆

所有的线性变换,本质上都是“旋转 沿正交轴拉伸 再旋转”。

想象你在平面上有一个完美的圆形橡皮印章(圆上有互相垂直的 轴网格线): 无论对这个印章施加多么复杂的矩阵变换,印章最终一定会变成一个倾斜的椭圆SVD 的核心几何发现是:在原始单位圆上,一定能找到两条原本相互垂直的直径向量 ,在经过复杂变换后,它们在变成椭圆时依然保持相互垂直(变成了椭圆的长半轴 和短半轴

矩阵分解 的三步拆解:

  1. (第一步旋转):把原始圆上那两根“变换后依然保持垂直”的特殊直径向量,旋转到标准坐标轴。
  2. (第二步拉伸):沿着这两个垂直轴方向进行缩放。缩放的倍数就是奇异值 (即椭圆的长短半轴长度)。
  3. (第三步旋转):把拉伸好的标准正交椭圆,整体旋转到输出空间中的最终倾斜角度。

SVD 在粗配准 / 绝对定向中的“剥离杂质”神技

在点云粗配准(如 FPFH 特征对齐)或控制点绝对定向中,我们需要从带噪匹配点对中寻找最优刚体旋转

跨协方差矩阵 记录了源点集与目标点集在各个方向上的协同运动趋势。然而,矩阵 不仅包含了旋转信息,还混杂了由于数据噪声带来的拉伸与缩放杂质(而刚体配准绝不允许改变物体的形状与大小)。

此时,对 进行 SVD 分解:

  • (奇异值矩阵):代表了形变与拉伸的幅度(需要剥离的杂质)。
  • (左右奇异向量):代表了源点集与目标点集各自的正交主轴姿态。

剥离杂质提取纯旋转:我们直接将缩放矩阵 剔除(替换为单位矩阵 ),把左右正交姿态拼接在一起,即可闭式解出全局最优的纯刚体旋转矩阵:

记忆法则:跨协方差矩阵 是一个带拉伸杂质的旋转;对 做 SVD,把拉伸项 扔掉,拼合 就得到了纯净的刚体旋转!

注意:如果代码里协方差定义为 (行向量堆叠),结果会变为 。本质相同,只是矩阵布局不同,调用库函数时注意转置约定。


4.6 步骤四:平移与镜像修正

平移

旋转 求出后,平移量直接由两个质心决定:

text
源质心 ——旋转 R——→ 新位置 ——平移 t——→ 目标质心

镜像修正(手性检测)

有时由于数据噪声,,说明结果里混入了镜像翻转——相当于把左手变成了右手。刚体运动不允许镜像,需要修正:

检查 是否为 ;若是,把第三个轴取反即可恢复为合法旋转。


4.7 完整流程回顾

整个 Kabsch 刚体配准可以用六步概括:

text
1. 计算源点集质心 p̄ 和目标点集质心 q̄
2. 把两组点各自平移到原点(去耦合)
3. 构造跨协方差矩阵 H = Σ p̂_i q̂_i^T
4. 对 H 做 SVD,用 R* = U V^T 得到旋转
5. 检查 det(R*),必要时修正镜像
6. 用 t* = q̄ - R* p̄ 得到平移

工程伪代码

cpp
// 输入:对应点集 P, Q(各 N 个点)
// 输出:旋转 R,平移 t

p_bar = mean(P);
q_bar = mean(Q);

P_hat = P - p_bar;   // 中心化
Q_hat = Q - q_bar;

H = P_hat^T * Q_hat; // 或 sum(p_hat_i * q_hat_i^T),注意库函数的约定
U, S, Vt = svd(H);

R = U * Vt;
if (det(R) < 0) {
    U.col(2) *= -1;    // 修正镜像
    R = U * Vt;
}

t = q_bar - R * p_bar;

4.8 概念辨析:Kabsch 算法、SVD 与 ICP 算法的层级关系

为了避免在工程实践中混淆底层工具与上层算法,在此厘清它们的层级包含关系角色分工

12 字核心心智模型

“质心处理平移,SVD 过滤拉伸,留下的就是纯旋转。”

层级包含图解

三者角色定位对比表

概念角色定位生活比喻在工程中的真实职责
SVD (奇异值分解)底层数学工具相当于**“计算器 / 扳手”**纯矩阵数学工具。只负责把矩阵分解为 ,本身不知道什么是点云或配准。
Kabsch 算法完整配准流程相当于**“拆卸引擎的操作规范”**解决“已知对应点对,求解刚体变换”的完整 6 步算法流程。在第 4 步调用 SVD 工具剥离拉伸。
ICP 算法迭代循环框架相当于**“自动巡航系统”**点云配准框架。在不知道点对匹配时,循环寻找最近邻点,每次循环内部调用 Kabsch 算法更新位姿。

终极解惑总结

  1. Kabsch 算法 SVD:Kabsch 算法是“配准流程解法”,SVD 是 Kabsch 算法在第 4 步用来提取旋转的“底层工具”。
  2. 粗配准与精配准的底层统一性与差异
    • 底层算法高度统一:无论是粗配还是精配,解算刚体姿态的数学核心都是 Kabsch 算法(基于 SVD)
    • 粗配准(Coarse Registration):通过人工手动点击或 FPFH 特征匹配,获得了显式确定的绝对同名点对。因此只需运行 1 次 Kabsch 算法,即可一次性闭式解出初始的 与物理尺度
    • 精配准(ICP Registration):面对海量无对应关系的点云,一开始不知道谁对应谁。因此在外部构建一个**“寻找临近点 调用 Kabsch 求解 变换点云 检查残差”**的迭代循环。在每一轮循环中,把当前寻找到的最近邻点当作匹配点对,不断重复执行 Kabsch 算法,直至重投影残差变化量小于阈值(,算法收敛)或达到最大迭代次数,最终输出精确解。

5. 补充:图形学与三维工程中的高频核心数学应用

在三维引擎开发(Unity/UE/OpenGL)和三维点云处理(PCL/VTK)的工程实践中,以下几个数学工具是极具实战经验和高频考察的重点:

5.1 法线变换矩阵的数学推导(Normal Matrix)

在 Shader 编程中,当我们要把顶点的法线 从模型空间转换到世界空间时,为什么不能直接乘以模型矩阵

  • 原因:如果模型矩阵 包含非均匀缩放(例如 X 轴拉伸 2 倍,Y 轴不变),直接用 乘以法线会导致变换后的法线不再垂直于变换后的表面

数学推导:

设表面上的一个切向量为 ,法向量为 。由于它们在模型空间下相互垂直,内积为 0:

当模型经过矩阵 变换后,切向量变为 。 设我们要寻找的法线变换矩阵为 ,变换后的法线为 。 变换后,切线与法线仍然必须保持正交:

对比原方程 ,只要满足以下条件,等式必然恒成立:

  • 结论:用于变换法线的正确矩阵是模型矩阵逆矩阵的转置(Transpose of the Inverse)。如果变换只包含旋转和均匀缩放,则可以直接使用 并进行归一化。
  • 实战场景(非均匀缩放下的光照异常):在场景编辑中,美术把一个标准球体沿 X 轴拉伸成椭球充当飞船模型。如果引擎的 Shader 仍直接用模型矩阵变换法线,椭球表面的光照会发生异常偏折(法线随网格歪斜,不再垂直于表面),必须使用逆转置矩阵才能正确还原 PBR 渲染效果。
N均匀球体Scale X错误法线正确法线非均匀缩放后的椭球
图 3. 非均匀缩放下的法线畸变与修正

5.2 射线与三角形相交检测(Möller-Trumbore 算法)

在鼠标点击选中物体(Mouse Picking)、射线检测以及碰撞检测中,求一条三维射线与三角形是否相交是最高频的计算。

射线方程为:

其中 为射线起点, 为方向单位向量。 三角形内部任意一点的重心坐标表示为:

联立方程 ,整理得到线性方程组:

使用克莱姆法则(Cramer's Rule)求解该线性方程组,可以高效计算出三个未知数 (相交距离)、。如果解满足约束条件,则判定为相交。这是目前三维引擎底层最通用的光线三角形求交算法。


5.3 旋转表达:欧拉角、万向节死锁与四元数(Quaternion)

在骨骼动画和相机轨道追踪中,如何表示物体的三维旋转是一个经典难题。

1. 欧拉角 (Euler Angles) 与万向节死锁 (Gimbal Lock)

欧拉角是将旋转分解为绕三个局部轴(Pitch、Yaw、Roll)的三次旋转。

  • 致命缺陷:当某一个轴旋转了 时,会使得另外两个旋转轴重合,从而丢失一个自由度(Degree of Freedom)。此时物体将无法在某些方向上进行平滑旋转,称为万向节死锁

2. 四元数 (Quaternion)

为了彻底消除死锁,图形学采用四元数 来表示旋转,这本质上是把旋转映射到四维超球面上。

  • Slerp(球面线性插值):由于四元数表示旋转的连续性,我们在对两个姿态进行过渡时,使用 Slerp 插值可以保证旋转角速度是恒定均匀的,而欧拉角插值会导致严重的旋转速度忽快忽慢。

5.4 重心坐标插值(Barycentric Interpolation)

当光栅化阶段确定了一个像素属于某个三角形后,GPU ... 需要计算该像素的颜色、法线以及纹理坐标(UV)。这些属性在顶点上是已知的,而三角形内部像素的值需要通过重心坐标 进行线性插值:

利用重心坐标,渲染管线可以完美平滑地在三角形表面渲染出渐变颜色、平滑法线(Phong Shading)和高精度的贴图纹理。


6. 总结

图形学和三维几何计算的精髓在于空间思维与几何直观

  • 点乘与叉乘提供了光照、朝向以及投影的快速几何度量
  • 齐次坐标TRS 变换顺序构成了整个虚拟世界三维仿射变换的通用流水线。
  • 最小二乘的核心就是"让误差平方和最小";SVD 刚体配准则是"找主方向、对齐方向、得到旋转",六步流程(质心对齐 → 构造 H → SVD → 求 R → 修正镜像 → 求 t)是 ICP 等点云算法的数学基石。

掌握这些底层的数学机理,能让你在编写 C++ 图形学算法、调试 Shader 以及优化点云 SLAM 匹配性能时,拥有更加清晰的直觉与洞察力。

基于 VitePress 强力驱动 | 记录技术与生活