概念页
点与向量运算
Points & Vector Operations
游戏程序员最常用的数学是三维向量与矩阵(三维线性代数)。本页按 GEA 第 5 章整理点与向量的基础:坐标系与手性、点与方向的区别、标量乘与 Hadamard 积、加减、模长与归一化、点积与投影、叉积、伪向量与外代数,以及线性插值 (lerp)。贯穿全章的两条经验是:能在二维里想清楚的问题先在二维里想,以及能不开方就不开方。
先在二维里解决(卷 I 5.1)
大多数向量运算在 2D 与 3D 中同样成立,所以可以先画二维图想清楚,再推广到三维;有时二维结果直接适用,有时能找到一个让问题真正变成二维的坐标系。例外:叉积只在 3D 中定义,有些问题必须三维才有意义。
坐标系(卷 I 5.2.1)
| 坐标系 | 参数 | 说明 |
|---|---|---|
| 笛卡尔坐标 (Cartesian) | 两两垂直的轴,游戏中最常用 | |
| 柱坐标 (Cylindrical) | 竖直高度轴 、径向 、偏航角 | |
| 球坐标 (Spherical) | 俯仰角 、偏航角 、半径 |
要选最贴合问题的坐标系。作者在 Midway 的《Crank the Weasel》里让战利品绕角色螺旋飞入:在以角色为原点的柱坐标中给 恒定角速度、 很小的向内速度、 很小的向上速度,就得到好看的螺旋动画,比用笛卡尔坐标建模简单得多。
左手系与右手系(卷 I 5.2.2)
- 右手系:右手四指绕 z 轴、拇指指向 +z 时,四指从 x 转向 y;左手系同理用左手。两者只差一根轴的方向(如 y 上、x 右时,右手系 z 指出纸面,左手系 z 指入纸面),翻转任一根轴即可互换。
- 数学规则不随手性改变,改变的只是数字到三维空间的「想象映射」。例外是物理模拟中的叉积,因为叉积结果是伪向量(见下)。
- 轴的朝向完全由人约定,选定后保持一致即可。GEA 说 3D 图形程序员通常用左手系:y 上、x 右、+z 指向相机前方,于是 z 越大越深,正合 z 缓冲深度遮挡的需要。
- 与 RTR4 的分歧:齐次坐标与矩阵约定 记录 RTR4 使用右手系、相机沿 看,并说 DirectX 等使用左手系;GEA 则把左手、+z 向前描述为图形程序员的典型做法,同时承认 OpenGL 等使用右手观察空间(卷 I 5.3.11.3)。两书都认为这只是约定。
点与向量(卷 I 5.2.3)
- 向量有大小和方向,可画成从尾到头的有向线段;标量只有大小。向量可以任意平移,只要大小和方向不变就是同一个向量。
- 尾部固定在原点的向量叫位置向量 (position vector) 或半径向量,用来表示点。可以说点是绝对的,向量是相对的。
- 多数游戏程序员和数学库把点和方向都叫「vector」;GEA 在需要区分时称「方向向量」。两者在转换为齐次坐标时处理不同(点 ,方向 ),混用会导致 bug。
- 笛卡尔基向量:沿 x、y、z 的单位向量 ,任意向量都可写成它们的线性组合,如 。
基本运算(卷 I 5.2.4.1–5.2.4.6)
- 标量乘:,只改长度不改方向,乘 翻转方向。
- 非均匀缩放与 Hadamard 积:。缩放向量 只是 3×3 对角缩放矩阵 的紧凑写法,即 。RGB 颜色相乘也用 Hadamard 积。
- 加减:逐分量相加减;几何上把 的尾接在 的头上。点与方向的规则:
| 运算 | 结果 |
|---|---|
| 方向 ± 方向 | 方向 |
| 点 + 方向 | 点 |
| 点 − 点 | 方向 |
| 点 + 点 | 无意义 |
- 模长:。
- 例:显式欧拉积分:(严格说只在速度恒定时成立)。
- 例:球-球相交:,若 则相交。开方在多数计算机上很贵,应比较 与 。比较相对长度或与另一个平方量比较时,用平方模长;不需要时绝不开方。
- 归一化:,即乘以模长的倒数。
- 法向量 (normal) 指垂直于表面的向量,不一定是单位长度;「归一化 (normalized)」指单位长度。两个术语不要混淆。平面可由一点加一个法向量定义,光照大量依赖法向量。
点积与投影(卷 I 5.2.4.7)
游戏中最常用两种向量乘法:点积(又称标量积 scalar product、内积 inner product)与叉积(又称向量积 vector product;GEA 还称其为「外积 outer product」,见下文原书问题)。
- 满足交换律、对加法的分配律,。
- 若 , 是 在 方向直线上的投影长度。
- 。
常用测试:
| 条件 | 含义 |
|---|---|
| 共线同向(单位向量时为 +1) | |
| 共线反向(单位向量时为 −1) | |
| 垂直 | |
| 夹角小于 90° | |
| 夹角大于 90° |
应用:
- 敌人在前还是在后:,与玩家朝向 (可直接从模型到世界矩阵中取出,见 坐标空间)点积,正为前、负为后。
- 点到平面的高度:平面由面上一点 与单位法线 定义,(书中举登月游戏为例)。平面方程形式见 几何图元的表示。
叉积(卷 I 5.2.4.8)
- 结果垂直于两个输入向量,只在三维中定义。
- 原书问题:GEA 把叉积别称为「outer product」。在线性代数的通行用法里,outer product(外积 / 张量积)指 ,结果是矩阵而非向量;与叉积相关的是 exterior/wedge product(楔积),且两者在三维中也只是 Hodge 对偶关系()而非同一对象。来源:Wikipedia「Outer product」「Cross product」「Pseudovector」条目。,等于以 为边的平行四边形面积;三角形面积 。
- 方向:右手系用右手定则,左手系用左手定则。换手性时所有数值不变、只是可视化沿翻转的轴镜像,所以恰好沿该轴的叉积在想象中也要翻过来。
- 性质:反交换 ,对加法分配,。
- 基向量:,,。正旋转是 x→y(绕 z)、y→z(绕 x)、z→x(绕 y);绕 y 的顺序「绕回来」,这就是绕 y 的旋转矩阵看起来与另两个互为转置的原因(见 基本仿射变换)。
应用:
- 由朝向构造正交基:已知物体朝向 且无横滚,,,三者即可组成朝向矩阵。
- 三角形法线:。
- 力矩:力 作用点相对质心为 ,力矩 ;力偏离质心才会产生转动。
伪向量与外代数(卷 I 5.2.4.9)
- 叉积的结果是伪向量 (pseudovector)。在平移、旋转、缩放下与普通向量无异;只有反射坐标系(如左右手互换)时才显出区别:普通向量变为镜像,伪向量变为镜像且反向。
- 真向量(极向量 / 逆变向量):位置及其各阶导数(速度、加速度、急动度 jerk)。伪向量(轴向量 / 协变向量 / 二重向量 bivector / 2-blade):角速度、磁场、由叉积求出的三角形表面法线。
- 外代数 / 格拉斯曼代数 (Grassmann algebra):引入楔积 。一次楔积得到伪向量,等价于叉积,表示两向量所成平行四边形的有向面积; 等价于标量三重积 ,得到伪标量 (pseudoscalar)(三重向量 / 3-blade),即平行六面体的有向体积。叉积、三重积与矩阵行列式因此相互关联。
- 原书问题:
- GEA 说在平移、旋转、缩放下看不出伪向量与向量的区别。这只对均匀缩放成立:一般线性变换 下 ,所以非均匀缩放下叉积(如法线)已经不能按普通向量变换——这也是 GEA 自己后文要求法线用逆转置矩阵的原因。来源:Wikipedia「Cross product」条目(Algebraic properties 中的矩阵恒等式)。
- GEA 把「协变向量 (covariant vector)」列为伪向量的别名、把「逆变向量」列为真向量的别名。协变 / 逆变描述的是在基变换下分量如何变换(向量 vs 对偶向量 / 余向量),与在反射下是否多一个符号(极向量 vs 轴向量)是两套独立的概念,不应等同。来源:Wikipedia「Covariance and contravariance of vectors」「Pseudovector」条目。
- 「楔积等价于叉积」严格说是 Hodge 对偶(二重向量与其对偶向量),见上。
- 「Grassman」应拼作 Grassmann(Hermann Grassmann)。
- 对游戏程序员主要意义在于理解点积和叉积,并记住某些向量其实是伪向量,变换时要特殊处理——这正是法线要用逆转置矩阵变换的另一种解释(见 基本仿射变换)。书中提到有团队在尝试直接在引擎中使用格拉斯曼代数。
线性插值 lerp(卷 I 5.2.5)
- 几何上 位于线段 上 处;数学上是权重为 与 的加权平均,权重之和为 1(任何加权平均的要求)。
- 例:以 30 帧/秒在 2 秒内从 A 平滑移到 B,需要 60 个中间位置。
- 旋转的插值(四元数 lerp/slerp)见 四元数。
小结:取舍与常见坑
| 坑 | 建议 |
|---|---|
| 把点和方向混为一谈 | 即使数学库不区分,自己心里要区分;齐次化时 不同 |
| 不必要的开方 | 比较距离时用平方模长 |
| 混淆 normal 与 normalized | 法向量不一定是单位长度 |
| 换手性后叉积方向 | 数值不变,可视化时用对应手的定则;物理中伪向量要特殊处理 |
PBRT 的视角:类型安全的几何类与数值稳健的向量运算(PBRT 4e 3.1–3.6)
坐标系与手性(3.1)
坐标系由原点 和 个线性无关的基向量组成的标架 (frame) 定义:向量 ,点 ,所以点和向量虽然都存三个数,却不能随意互换。定义标架本身又需要点和向量,于是需要一个原点 、基为三根单位轴的规范标架,即世界空间。pbrt 使用左手系。
| 书 | 手性约定 |
|---|---|
| RTR4 | 右手系,相机沿 看(见 齐次坐标与矩阵约定) |
| GEA4 | 认为图形程序员"通常"用左手系、+z 指向前方(上文) |
| PBRT 4e | 左手系,相机空间沿 看(3.1.1、3.9.9) |
| 三书都承认这只是约定;PBRT 的特殊之处在于手性会进入代码:变换翻转手性时要翻转法线(见 基本仿射变换 的 PBRT 小节)。 |
用类型系统区分点、向量和法线(3.2–3.5)
Tuple2/Tuple3模板同时以分量类型和子类类型为参数(CRTP),使operator+只接受同一子类、返回同一子类:两个Vector3相加得到Vector3,不可能把结果赋给Point3;分量类型按 C++ 提升规则(整数 +Float得Float)。分量是公开成员,因为隐藏"三个浮点数"这一设计毫无好处。调试版构造时用DCHECK检查 NaN,尽早发现错误。- 点 − 点 = 向量,点 + 向量 = 点;法线不能加到点上,两条法线也不能做叉积;法线不一定是单位长度。
FaceForward(n, v)把法线翻到 所在半球;两个参数都是Vector3的版本要注意参数顺序。 - 光线 ,另存时间和起点所在介质;
RayDifferential多带 、 方向各偏一个像素的两条辅助光线,供纹理估计滤波面积。每像素多样本时要按样本间距缩小微分,否则纹理会过糊。
数值稳健的写法(3.3)
- 夹角:两单位向量接近平行或反向时 精度很差。pbrt 的
AngleBetween()用 (点积为负时用 ),在原点附近计算,浮点精度更高 (Hatch 2003)。本页用 float32 实测:真实夹角 rad 时acos得到 (误差约 15%),而 arcsin 写法得到 。 - 叉积:用
DifferenceOfProducts(a,b,c,d)计算 (附录 B.2.9)以减少相消误差;pbrt 早期版本曾不得不用双精度算叉积来消除图像瑕疵。 即平行四边形面积。 - Gram–Schmidt:(式 3.2),要求 已归一化。
- 由一个向量建坐标系:对单位向量 , 与 和 构成正交标架(Frisvad 2012;本页对随机单位向量验算三者两两正交、均为单位长)。 时 误差大,pbrt 用带
copysign的改写,书中说它由 Duff 等 (2017) 和 Max (2017) 同时独立得出。该式在线版曾把"1−"错放进分子,经 pbr-book-website issue #18 指出后已修正(https://github.com/mmp/pbr-book-website/issues/18,已关闭)。
出处
- Game_Engine_Architecture_4Ed_-_Jason_Gregory.pdf
- Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 I 第 5 章 5.1–5.2 节
- PBRT 4e source excerpt: chapter 03 (Geometry and Transformations)
- Pharr, Jakob, Humphreys《Physically Based Rendering: From Theory to Implementation》第 4 版(MIT Press, 2023)第 3 章 3.1–3.6 节,pbr-book.org/4ed/Geometry_and_Transformations
相关页面
游戏数学齐次坐标与矩阵约定Homogeneous Coordinates & Matrix Conventions实时渲染用 4×4 矩阵和四元素齐次坐标统一表示点和方向:方向向量 w=0,点 w=1。这样线性变换与平移可以合成为仿射变换并连乘。RTR4 采用列向量记法、右手观察坐标系;DirectX 采用行向量记法,书中强调这只是记法差异。游戏数学基本仿射变换Basic Affine Transforms平移、旋转、缩放、切变是摆放和变形物体的基础矩阵;它们的连乘顺序有关、但满足结合律。刚体变换的逆可以直接写出,法线要用伴随矩阵的转置(传统说法是逆转置)变换。理解这些矩阵的性质能写出更快、更正确的变换代码。图形学坐标空间Coordinate Spaces顶点从模型到屏幕要依次经过模型空间、世界空间、观察空间、裁剪空间、标准化设备坐标 (NDC)、窗口坐标。每一步变换都有明确目的,大部分可以用 4×4 矩阵表示并相乘合并。游戏数学几何图元的表示Geometric Primitive Representations除点、向量、矩阵、四元数外,游戏引擎还天天用到直线/射线/线段、球、平面、AABB、OBB、视锥和凸多面体区域。本页按 GEA 整理它们在内存中如何表示以及最基本的「点在内部吗」测试;各种求交与重叠算法见 RTR4 来源的相交测试系列页面。游戏开发SIMD 向量化SIMD VectorizationSIMD(单指令多数据)用一条机器指令并行处理多个数据。x86 上从 MMX 发展到 SSE(128 位,4 个 float)、AVX(256 位,8 个)和 AVX-512(512 位,16 个)。GEA 用 SSE 内建函数演示:数组相加、点积、向量×矩阵、矩阵×矩阵,强调两点——避免寄存器内的水平相加(先转置数据),以及把 SIMD 寄存器看成 N 条并行的「通道 (lane)」而不只是一个 4D 向量;条件分支则用向量谓词化 (vector predication / select) 代替。游戏数学直线与平面求交Line & Plane Intersections本页收录 RTR4 第 22.15~22.16 节的基础几何工具:二维直线/光线/线段求交(垂直点积法与无除法的早期拒绝法)、三维直线求交与异面直线最近点,以及三个平面的交点公式(可用于求 k-DOP 等平面围成凸体的角点)。这些在游戏中常用于射线检测、二维几何、碰撞与包围体构建。游戏数学包围体重叠测试Bounding Volume Overlap Tests碰撞检测先测试包围体是否重叠,只有重叠时才逐图元测试;最底层则需要三角形之间、三角形与盒之间的精确测试。本页整理 RTR4 第 22.10~22.13 节:平面与 AABB/OBB(只测两个角的"投影半径"法)、三角形-三角形(Guigue-Devillers 行列式法)、三角形-AABB(13 轴 SAT)、球-球、球-盒(Arvo 及其向量化版本)、AABB-AABB、k-DOP-k-DOP 与 OBB-OBB(15 轴 SAT)。更复杂的 BV(OBB、k-DOP)空体积更少、更紧,代价是测试更贵。游戏数学伪随机数生成Pseudorandom Number Generation游戏引擎到处要用随机数,但随机数生成器 (RNG) 并不真正随机:它只是给出一个复杂但完全确定、预先定义的序列,因此严格说应叫伪随机数生成器 (PRNG)。好坏取决于周期(多久重复)和能否通过各种公认的随机性测试。GEA 介绍了线性同余、梅森旋转、Marsaglia 的 Mother-of-All / Xorshift / KISS99,以及 PCG。游戏开发刚体动力学Rigid Body Dynamics游戏物理引擎关心的是动力学:力如何影响运动。它做了两个简化:经典(牛顿)力学(无量子、无相对论效应)和刚体(形状不变)。无约束刚体有 6 个自由度,其运动可以分成互相独立的线性动力学(质心像质点一样平动)和角动力学(绕质心转动)。线性部分是 F=ma 的常微分方程;三维角动力学则以角动量为主变量,并用一个四元数形式的微分方程更新朝向(GEA4 卷 II 14.4)。游戏数学四元数Quaternion单位四元数可以紧凑地表示任意三维旋转,与轴-角表示互相转换很简单,并能做稳定、匀速的朝向插值(slerp),这些都是欧拉角做不好的。它可以高效地转换为矩阵以便与其他变换合并。游戏数学球面几何与方向参数化Spherical Geometry & Direction Parameterizations渲染中大量计算发生在单位球面上:方向就是球面上的点,一组方向就是球面上的一块区域。本页按 PBRT 4e 3.8 节整理立体角、球面多边形面积、三种球面参数化(球坐标、八面体编码、等面积映射)以及用于剔除光源的方向锥,并附原书一处公式问题。