中EN
概念页

点与向量运算

Points & Vector Operations

游戏程序员最常用的数学是三维向量与矩阵(三维线性代数)。本页按 GEA 第 5 章整理点与向量的基础:坐标系与手性、点与方向的区别、标量乘与 Hadamard 积、加减、模长与归一化、点积与投影、叉积、伪向量与外代数,以及线性插值 (lerp)。贯穿全章的两条经验是:能在二维里想清楚的问题先在二维里想,以及能不开方就不开方。

先在二维里解决(卷 I 5.1)

大多数向量运算在 2D 与 3D 中同样成立,所以可以先画二维图想清楚,再推广到三维;有时二维结果直接适用,有时能找到一个让问题真正变成二维的坐标系。例外:叉积只在 3D 中定义,有些问题必须三维才有意义。

坐标系(卷 I 5.2.1)

坐标系参数说明
笛卡尔坐标 (Cartesian)(Px,Py,Pz)(P_x,P_y,P_z)两两垂直的轴,游戏中最常用
柱坐标 (Cylindrical)(Ph,Pr,Pθ)(P_h,P_r,P_\theta)竖直高度轴 hh、径向 rr、偏航角 θ\theta
球坐标 (Spherical)(Pr,Pϕ,Pθ)(P_r,P_\phi,P_\theta)俯仰角 ϕ\phi、偏航角 θ\theta、半径 rr

要选最贴合问题的坐标系。作者在 Midway 的《Crank the Weasel》里让战利品绕角色螺旋飞入:在以角色为原点的柱坐标中给 θ\theta 恒定角速度、rr 很小的向内速度、hh 很小的向上速度,就得到好看的螺旋动画,比用笛卡尔坐标建模简单得多。

左手系与右手系(卷 I 5.2.2)

  • 右手系:右手四指绕 z 轴、拇指指向 +z 时,四指从 x 转向 y;左手系同理用左手。两者只差一根轴的方向(如 y 上、x 右时,右手系 z 指出纸面,左手系 z 指入纸面),翻转任一根轴即可互换。
  • 数学规则不随手性改变,改变的只是数字到三维空间的「想象映射」。例外是物理模拟中的叉积,因为叉积结果是伪向量(见下)。
  • 轴的朝向完全由人约定,选定后保持一致即可。GEA 说 3D 图形程序员通常用左手系:y 上、x 右、+z 指向相机前方,于是 z 越大越深,正合 z 缓冲深度遮挡的需要。
  • 与 RTR4 的分歧:齐次坐标与矩阵约定 记录 RTR4 使用右手系、相机沿 −z-z 看,并说 DirectX 等使用左手系;GEA 则把左手、+z 向前描述为图形程序员的典型做法,同时承认 OpenGL 等使用右手观察空间(卷 I 5.3.11.3)。两书都认为这只是约定。

点与向量(卷 I 5.2.3)

  • 向量有大小和方向,可画成从尾到头的有向线段;标量只有大小。向量可以任意平移,只要大小和方向不变就是同一个向量。
  • 尾部固定在原点的向量叫位置向量 (position vector) 或半径向量,用来表示点。可以说点是绝对的,向量是相对的。
  • 多数游戏程序员和数学库把点和方向都叫「vector」;GEA 在需要区分时称「方向向量」。两者在转换为齐次坐标时处理不同(点 w=1w=1,方向 w=0w=0),混用会导致 bug。
  • 笛卡尔基向量:沿 x、y、z 的单位向量 i,j,k\mathbf{i},\mathbf{j},\mathbf{k},任意向量都可写成它们的线性组合,如 (5,3,−2)=5i+3j−2k(5,3,-2)=5\mathbf{i}+3\mathbf{j}-2\mathbf{k}。

基本运算(卷 I 5.2.4.1–5.2.4.6)

  • 标量乘:sa=(sax,say,saz)s\mathbf{a}=(sa_x,sa_y,sa_z),只改长度不改方向,乘 −1-1 翻转方向。
  • 非均匀缩放与 Hadamard 积:s⊗a=(sxax,syay,szaz)\mathbf{s}\otimes\mathbf{a}=(s_xa_x,s_ya_y,s_za_z)。缩放向量 s\mathbf{s} 只是 3×3 对角缩放矩阵 S\mathbf{S} 的紧凑写法,即 aS\mathbf{a}\mathbf{S}。RGB 颜色相乘也用 Hadamard 积。
  • 加减:逐分量相加减;几何上把 b\mathbf{b} 的尾接在 a\mathbf{a} 的头上。点与方向的规则:
运算结果
方向 ± 方向方向
点 + 方向点
点 − 点方向
点 + 点无意义
  • 模长:∣a∣=ax2+ay2+az2|\mathbf{a}|=\sqrt{a_x^2+a_y^2+a_z^2}。
  • 例:显式欧拉积分:P2=P1+vΔt\mathbf{P}_2=\mathbf{P}_1+\mathbf{v}\Delta t(严格说只在速度恒定时成立)。
  • 例:球-球相交:d=C2−C1\mathbf{d}=\mathbf{C}_2-\mathbf{C}_1,若 ∣d∣<r1+r2|\mathbf{d}|<r_1+r_2 则相交。开方在多数计算机上很贵,应比较 ∣d∣2|\mathbf{d}|^2 与 (r1+r2)2(r_1+r_2)^2。比较相对长度或与另一个平方量比较时,用平方模长;不需要时绝不开方。
  • 归一化:u=v/∣v∣\mathbf{u}=\mathbf{v}/|\mathbf{v}|,即乘以模长的倒数。
  • 法向量 (normal) 指垂直于表面的向量,不一定是单位长度;「归一化 (normalized)」指单位长度。两个术语不要混淆。平面可由一点加一个法向量定义,光照大量依赖法向量。

点积与投影(卷 I 5.2.4.7)

游戏中最常用两种向量乘法:点积(又称标量积 scalar product、内积 inner product)与叉积(又称向量积 vector product;GEA 还称其为「外积 outer product」,见下文原书问题)。 a⋅b=axbx+ayby+azbz=∣a∣∣b∣cos⁡θ\mathbf{a}\cdot\mathbf{b}=a_xb_x+a_yb_y+a_zb_z=|\mathbf{a}||\mathbf{b}|\cos\theta

  • 满足交换律、对加法的分配律,sa⋅b=a⋅sb=s(a⋅b)s\mathbf{a}\cdot\mathbf{b}=\mathbf{a}\cdot s\mathbf{b}=s(\mathbf{a}\cdot\mathbf{b})。
  • 若 ∣u∣=1|\mathbf{u}|=1,a⋅u\mathbf{a}\cdot\mathbf{u} 是 a\mathbf{a} 在 u\mathbf{u} 方向直线上的投影长度。
  • ∣a∣2=a⋅a|\mathbf{a}|^2=\mathbf{a}\cdot\mathbf{a}。

常用测试:

条件含义
a⋅b=ab\mathbf{a}\cdot\mathbf{b}=ab共线同向(单位向量时为 +1)
a⋅b=−ab\mathbf{a}\cdot\mathbf{b}=-ab共线反向(单位向量时为 −1)
a⋅b=0\mathbf{a}\cdot\mathbf{b}=0垂直
a⋅b>0\mathbf{a}\cdot\mathbf{b}>0夹角小于 90°
a⋅b<0\mathbf{a}\cdot\mathbf{b}<0夹角大于 90°

应用:

  • 敌人在前还是在后:v=E−P\mathbf{v}=\mathbf{E}-\mathbf{P},与玩家朝向 f\mathbf{f}(可直接从模型到世界矩阵中取出,见 坐标空间)点积,正为前、负为后。
  • 点到平面的高度:平面由面上一点 Q\mathbf{Q} 与单位法线 n\mathbf{n} 定义,h=(P−Q)⋅nh=(\mathbf{P}-\mathbf{Q})\cdot\mathbf{n}(书中举登月游戏为例)。平面方程形式见 几何图元的表示。

叉积(卷 I 5.2.4.8)

a×b=(aybz−azby, azbx−axbz, axby−aybx)\mathbf{a}\times\mathbf{b}=(a_yb_z-a_zb_y,\ a_zb_x-a_xb_z,\ a_xb_y-a_yb_x)

  • 结果垂直于两个输入向量,只在三维中定义。
  • 原书问题:GEA 把叉积别称为「outer product」。在线性代数的通行用法里,outer product(外积 / 张量积)指 abT\mathbf{a}\mathbf{b}^T,结果是矩阵而非向量;与叉积相关的是 exterior/wedge product(楔积),且两者在三维中也只是 Hodge 对偶关系(a∧b=i a×b\mathbf{a}\wedge\mathbf{b}=i\,\mathbf{a}\times\mathbf{b})而非同一对象。来源:Wikipedia「Outer product」「Cross product」「Pseudovector」条目。∣a×b∣=∣a∣∣b∣sin⁡θ|\mathbf{a}\times\mathbf{b}|=|\mathbf{a}||\mathbf{b}|\sin\theta,等于以 a,b\mathbf{a},\mathbf{b} 为边的平行四边形面积;三角形面积 A=12∣(V2−V1)×(V3−V1)∣A=\tfrac12|(\mathbf{V}_2-\mathbf{V}_1)\times(\mathbf{V}_3-\mathbf{V}_1)|。
  • 方向:右手系用右手定则,左手系用左手定则。换手性时所有数值不变、只是可视化沿翻转的轴镜像,所以恰好沿该轴的叉积在想象中也要翻过来。
  • 性质:反交换 a×b=−(b×a)\mathbf{a}\times\mathbf{b}=-(\mathbf{b}\times\mathbf{a}),对加法分配,(sa)×b=a×(sb)=s(a×b)(s\mathbf{a})\times\mathbf{b}=\mathbf{a}\times(s\mathbf{b})=s(\mathbf{a}\times\mathbf{b})。
  • 基向量:i×j=k\mathbf{i}\times\mathbf{j}=\mathbf{k},j×k=i\mathbf{j}\times\mathbf{k}=\mathbf{i},k×i=j\mathbf{k}\times\mathbf{i}=\mathbf{j}。正旋转是 x→y(绕 z)、y→z(绕 x)、z→x(绕 y);绕 y 的顺序「绕回来」,这就是绕 y 的旋转矩阵看起来与另两个互为转置的原因(见 基本仿射变换)。

应用:

  • 由朝向构造正交基:已知物体朝向 klocal\mathbf{k}_{local} 且无横滚,ilocal=normalize(jworld×klocal)\mathbf{i}_{local}=\text{normalize}(\mathbf{j}_{world}\times\mathbf{k}_{local}),jlocal=klocal×ilocal\mathbf{j}_{local}=\mathbf{k}_{local}\times\mathbf{i}_{local},三者即可组成朝向矩阵。
  • 三角形法线:n=normalize((P2−P1)×(P3−P1))\mathbf{n}=\text{normalize}((\mathbf{P}_2-\mathbf{P}_1)\times(\mathbf{P}_3-\mathbf{P}_1))。
  • 力矩:力 f\mathbf{f} 作用点相对质心为 r\mathbf{r},力矩 N=r×f\mathbf{N}=\mathbf{r}\times\mathbf{f};力偏离质心才会产生转动。

伪向量与外代数(卷 I 5.2.4.9)

  • 叉积的结果是伪向量 (pseudovector)。在平移、旋转、缩放下与普通向量无异;只有反射坐标系(如左右手互换)时才显出区别:普通向量变为镜像,伪向量变为镜像且反向。
  • 真向量(极向量 / 逆变向量):位置及其各阶导数(速度、加速度、急动度 jerk)。伪向量(轴向量 / 协变向量 / 二重向量 bivector / 2-blade):角速度、磁场、由叉积求出的三角形表面法线。
  • 外代数 / 格拉斯曼代数 (Grassmann algebra):引入楔积 A∧B\mathbf{A}\wedge\mathbf{B}。一次楔积得到伪向量,等价于叉积,表示两向量所成平行四边形的有向面积;A∧B∧C\mathbf{A}\wedge\mathbf{B}\wedge\mathbf{C} 等价于标量三重积 A⋅(B×C)\mathbf{A}\cdot(\mathbf{B}\times\mathbf{C}),得到伪标量 (pseudoscalar)(三重向量 / 3-blade),即平行六面体的有向体积。叉积、三重积与矩阵行列式因此相互关联。
  • 原书问题:
    • GEA 说在平移、旋转、缩放下看不出伪向量与向量的区别。这只对均匀缩放成立:一般线性变换 M\mathbf{M} 下 (Ma)×(Mb)=det⁡(M) M−T(a×b)(\mathbf{M}\mathbf{a})\times(\mathbf{M}\mathbf{b})=\det(\mathbf{M})\,\mathbf{M}^{-T}(\mathbf{a}\times\mathbf{b}),所以非均匀缩放下叉积(如法线)已经不能按普通向量变换——这也是 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)

L=lerp(A,B,β)=(1−β)A+βB,0≤β≤1\mathbf{L}=\text{lerp}(\mathbf{A},\mathbf{B},\beta)=(1-\beta)\mathbf{A}+\beta\mathbf{B},\quad 0\le\beta\le1

  • 几何上 L\mathbf{L} 位于线段 ABAB 上 100β%100\beta\% 处;数学上是权重为 (1−β)(1-\beta) 与 β\beta 的加权平均,权重之和为 1(任何加权平均的要求)。
  • 例:以 30 帧/秒在 2 秒内从 A 平滑移到 B,需要 60 个中间位置。
  • 旋转的插值(四元数 lerp/slerp)见 四元数。

小结:取舍与常见坑

坑建议
把点和方向混为一谈即使数学库不区分,自己心里要区分;齐次化时 ww 不同
不必要的开方比较距离时用平方模长
混淆 normal 与 normalized法向量不一定是单位长度
换手性后叉积方向数值不变,可视化时用对应手的定则;物理中伪向量要特殊处理

PBRT 的视角:类型安全的几何类与数值稳健的向量运算(PBRT 4e 3.1–3.6)

坐标系与手性(3.1)

坐标系由原点 pop_o 和 nn 个线性无关的基向量组成的标架 (frame) 定义:向量 v=s1v1+⋯+snvnv=s_1v_1+\cdots+s_nv_n,点 p=po+s1v1+⋯+snvnp=p_o+s_1v_1+\cdots+s_nv_n,所以点和向量虽然都存三个数,却不能随意互换。定义标架本身又需要点和向量,于是需要一个原点 (0,0,0)(0,0,0)、基为三根单位轴的规范标架,即世界空间。pbrt 使用左手系。

书手性约定
RTR4右手系,相机沿 −z-z 看(见 齐次坐标与矩阵约定)
GEA4认为图形程序员"通常"用左手系、+z 指向前方(上文)
PBRT 4e左手系,相机空间沿 +z+z 看(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) 把法线翻到 vv 所在半球;两个参数都是 Vector3 的版本要注意参数顺序。
  • 光线 r(t)=o+t d, 0≤t<∞r(t)=o+t\,d,\ 0\le t<\infty,另存时间和起点所在介质;RayDifferential 多带 xx、yy 方向各偏一个像素的两条辅助光线,供纹理估计滤波面积。每像素多样本时要按样本间距缩小微分,否则纹理会过糊。

数值稳健的写法(3.3)

  • 夹角:两单位向量接近平行或反向时 arccos⁡(v⋅w)\arccos(v\cdot w) 精度很差。pbrt 的 AngleBetween() 用 2arcsin⁡(∥v−w∥/2)2\arcsin(\|v-w\|/2)(点积为负时用 π−2arcsin⁡(∥v+w∥/2)\pi-2\arcsin(\|v+w\|/2)),在原点附近计算,浮点精度更高 (Hatch 2003)。本页用 float32 实测:真实夹角 3×10−43\times10^{-4} rad 时 acos 得到 3.45×10−43.45\times10^{-4}(误差约 15%),而 arcsin 写法得到 3.0×10−43.0\times10^{-4}。
  • 叉积:用 DifferenceOfProducts(a,b,c,d) 计算 ab−cdab-cd(附录 B.2.9)以减少相消误差;pbrt 早期版本曾不得不用双精度算叉积来消除图像瑕疵。∥v×w∥=∥v∥∥w∥∣sin⁡θ∣\|v\times w\|=\|v\|\|w\||\sin\theta| 即平行四边形面积。
  • Gram–Schmidt:v⊥=v−(v⋅w^)w^v_\perp=v-(v\cdot\hat w)\hat w(式 3.2),要求 ww 已归一化。
  • 由一个向量建坐标系:对单位向量 vv,(1−vx21+vz,−vxvy1+vz,−vx)\big(1-\frac{v_x^2}{1+v_z},-\frac{v_xv_y}{1+v_z},-v_x\big) 与 (−vxvy1+vz,1−vy21+vz,−vy)\big(-\frac{v_xv_y}{1+v_z},1-\frac{v_y^2}{1+v_z},-v_y\big) 和 vv 构成正交标架(Frisvad 2012;本页对随机单位向量验算三者两两正交、均为单位长)。vz≈−1v_z\approx-1 时 1/(1+vz)1/(1+v_z) 误差大,pbrt 用带 copysign 的改写,书中说它由 Duff 等 (2017) 和 Max (2017) 同时独立得出。该式在线版曾把"1−"错放进分子,经 pbr-book-website issue #18 指出后已修正(https://github.com/mmp/pbr-book-website/issues/18,已关闭)。
  1. Game_Engine_Architecture_4Ed_-_Jason_Gregory.pdf
  2. Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 I 第 5 章 5.1–5.2 节
  3. PBRT 4e source excerpt: chapter 03 (Geometry and Transformations)
  4. 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 节整理立体角、球面多边形面积、三种球面参数化(球坐标、八面体编码、等面积映射)以及用于剔除光源的方向锥,并附原书一处公式问题。

被这些页面引用1 篇

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原