第 10 章 3D 图形数学专题 (Mathematical Topics from 3D Graphics)

      +

      核心结论

      • 图形管线概述:顶点 → 图元装配 → 裁剪 / 投影 → 光栅化 → 像素着色 → 输出;每阶段都有专属数学。

      • 3D 视图 = 4 个矩阵相乘:模型矩阵 × 视图矩阵 × 投影矩阵 × 视口矩阵(实际 GPU 中前两个合一为 model-view,第三与第四合一为 projection-viewport)。

      • 多边形网格:顶点 + 索引 + 邻接;索引网格节省内存;strip / fan 进一步压缩;拓扑查询需邻接结构。

      • 纹理映射:UV 坐标、采样器、mipmap、过滤模式;GPU 内建纹理单元完成;切线空间用于法线贴图。

      • 局部光照模型:环境光 + 漫反射 (n · l) + 镜面反射(Phong / Blinn-Phong)三部分叠加;渲染方程是物理正确的扩展。

      • 骨骼动画:每顶点 4 个骨骼权重 + 4×4 骨骼变换矩阵;GPU 蒙皮(skinning)在顶点着色器中完成。

      • 凹凸映射:法线贴图(扰动法向量)、视差贴图(深度位移)、环境光遮蔽(ambient occlusion);用纹理表达细节而无需更多三角形。

      本章主旨

      本章是全书的"图形应用章"——把第 1-9 章的数学工具落地到真实图形管线。涵盖视图构造、网格表达、纹理采样、光照模型、骨骼动画、凹凸贴图、完整 GPU 管线、HLSL shader 实例。这是全书最贴近实战的一章——学完本章应能"读懂 GPU shader 代码 + 解释每个矩阵 / 公式的来源"。

      一、核心概念

      本章围绕 7 个核心概念展开:从图形管线整体出发,依次介绍视图、网格、纹理、光照、动画、凹凸贴图,最后给完整 GPU 管线与 shader 示例。

      概念 定义 + 重要性 实现提示

      图形管线概述

      顶点 → 图元装配 → 顶点着色 → 裁剪 / 投影 → 光栅化 → 片段着色 → 输出。每阶段可编程(现代 GPU)或固定。

      §10.10;理解阶段划分才能定位性能瓶颈与数学作用点。

      3D 视图构造

      模型矩阵 → 视图矩阵(look-at) → 投影矩阵(透视 / 正交) → 视口变换;最终把世界点变换到屏幕像素。

      §10.2-§10.3;模型 / 视图矩阵是 4×4 齐次矩阵;投影矩阵决定视锥体形状。

      多边形网格

      顶点 + 索引列表 + 邻接信息;strip / fan 进一步压缩带宽;BVH / 八叉树加速查询。

      §10.4;FBX / glTF 导入后通常用索引三角网格存储。

      纹理映射

      UV 坐标把网格表面参数化到 [0, 1]² 纹理空间;采样 + 过滤 + mipmap 是 GPU 纹理单元的工作。

      §10.5;纹理单元硬件优化(缓存 / LOD);采样器状态决定过滤模式。

      局部光照模型

      环境光 + 漫反射 n · l + 镜面反射 pow(n · h, s);Blinn-Phong 用半向量 h = normalize(l + v) 简化计算。

      §10.6;Phong / Blinn-Phong 是经验模型;物理正确的 PBR 用 BRDF + 微表面模型。

      骨骼动画

      每顶点关联若干骨骼 + 权重;顶点位置 = Σ 权重_i · 骨骼_i · 顶点;GPU 蒙皮在 vertex shader 完成。

      §10.8;4 骨骼 / 顶点是 GPU 优化上限(uniform 寄存器约束)。

      凹凸映射 (Bump mapping)

      法线贴图(在切线空间编码扰动法向量)、视差贴图(位移)、位移贴图(实际几何位移);用纹理模拟细节而无需更多三角形。

      §10.9;切线空间 (T, B, N) 必备;法线贴图节省内存大幅超越额外几何。

      二、详细笔记

      2.1 图形管线概述 (Graphics Pipeline Overview)

      What:把 3D 场景转换为 2D 像素的多阶段流水线;现代 GPU 大部分阶段可编程(shader)。

      Why:理解管线才能定位"代码在哪一阶段"、"GPU 在做什么"、"性能瓶颈在哪"。

      How

      典型实时管线(§10.10):

      1. 设置场景:相机、光照、雾、深度缓冲。

      2. 可见性判定:粗筛(视锥剔除、八叉树 / BVH)。

      3. 对象级渲染状态:材质、纹理、着色器绑定。

      4. 几何生成 / 提交:顶点 / 索引数据送 GPU。

      5. 顶点级操作:model-view 变换、骨骼蒙皮、顶点光照(vertex shader)。

      6. 裁剪 / 投影 / 背面剔除:把 3D 三角形映射到 2D 屏幕空间;剔除背向三角形。

      7. 光栅化:把三角形离散为像素(fragment)。

      8. 片段级操作:纹理采样、像素光照、深度测试(fragment shader)。

      9. 输出合并:深度测试、混合、写入 framebuffer。

      每阶段的核心数学
      • 顶点变换:matrix × vector (§4, §5, §6)。

      • 投影:perspective division (§6.5)。

      • 光栅化:Bresenham / 重心坐标插值 (§9.6)。

      • 光照:n · l, n · h, pow() (§10.6)。

      • 纹理采样:UV 坐标 + 过滤 + mipmap。

      When:写 shader 时必须知道在哪一阶段;性能调优时定位瓶颈阶段。

      Example:GPU 顶点着色器对应"顶点级操作"阶段;片段着色器对应"片段级操作"阶段;几何 / 计算着色器对应更上层的"图元装配 / 通用计算"。

      2.2 3D 视图构造 (3D Viewing)

      What:4 个矩阵链把世界坐标变换到屏幕像素。

      Why:3D 场景到 2D 屏幕的核心数学;理解每个矩阵的作用才能定位"模型去哪了"。

      How

      变换链:

      \[\mathbf{P}_{\text{screen}} = \mathbf{P}_{\text{world}} \cdot \mathbf{M}_{\text{model}} \cdot \mathbf{M}_{\text{view}} \cdot \mathbf{M}_{\text{proj}} \cdot \mathbf{M}_{\text{viewport}}\]

      各矩阵作用:

      • 模型矩阵:物体 → 世界(§3.3 + §6.4 齐次)。

      • 视图矩阵(look-at):世界 → 相机;由相机位置 / 目标 / 上方向构造。

      • 投影矩阵:相机视锥 → NDC(§6.5);透视 / 正交。

      • 视口矩阵:NDC → 屏幕像素。

      视图矩阵构造(look-at):

      \[\mathbf{M}_{\text{view}} = \mathbf{R}_{\text{camera}} \cdot \mathbf{T}_{-\mathbf{c}_{\text{cam}}} \end{bmatrix}\]
      相机空间约定

      本书(左手系):+x 右、+y 上、+z 前;OpenGL(右手系):+x 右、+y 上、+z 后(朝向观察者);DirectX(左手系):+x 右、+y 上、+z 前。跨引擎时注意 z 翻转。

      When:所有 3D 渲染;VR / AR(眼盒 / FOV);shadow mapping(光视图矩阵)。

      Example:相机在 (0, 0, 5)、朝 (0, 0, 0)、上方向 (0, 1, 0)。视图矩阵 = 平移 (0, 0, -5) ——把相机移到原点。

      2.3 多边形网格 (Polygon Meshes)

      What:顶点 + 边 + 面的数据结构;通常用索引三角网格存储(节省内存)。

      Why:3D 模型的标准存储格式;GPU 输入格式;BVH / 邻接查询基础。

      How

      索引三角网格结构:

      数据 内容

      顶点数组

      Vec3[] positionsVec3[] normalsVec2[] uvs

      索引数组

      uint16_t[] indices,每 3 个索引 = 1 个三角形

      邻接(可选)

      边邻接、面邻接(用于拓扑查询)

      strip / fan 压缩:

      • Triangle stripindices = [0, 1, 2, 3, 4, …​],每 3 个连续索引 + 前两个 = 1 个三角形;节省索引 3 倍。

      • Triangle fan:以第 0 个顶点为中心,扇形展开;适合圆盘 / 锥体。

      索引 vs 直接三角形数组
      • 直接三角形:每 3 个顶点 = 1 个三角形;顶点重复率高(共享顶点存多份)。

      • 索引:顶点共享;GPU 通过索引复用顶点缓存;现代 GPU 默认使用索引。

      When:模型导入(FBX / glTF → 索引网格);渲染提交;碰撞器构造(凸包 / BVH)。

      Example:立方体 8 顶点 + 12 三角形 × 3 索引 = 8 顶点 + 36 索引(而非 36 顶点)。

      2.4 纹理映射 (Texture Mapping)

      What:把 2D 图像"贴"到 3D 网格表面;UV 坐标 (u, v) ∈ [0, 1]² 把网格顶点映射到纹理像素 (texel)。

      Why:用图像而非几何表达表面细节(颜色、法线、粗糙度等);节省内存远胜增加三角形。

      How

      UV 坐标 + 采样:

      操作 说明

      顶点 UV

      每个顶点存 (u, v);三角形内像素 UV = 重心坐标插值

      采样

      纹理单元根据 UV 取对应 texel 颜色

      过滤

      双线性 / 三线性 / 各向异性;处理"一个像素覆盖多 texel"(放大)或"多个像素覆盖一 texel"(缩小)

      Mipmap

      预生成多级 LOD;远处自动用低分辨率;解决 闪烁锯齿

      寻址模式

      wrap / clamp / mirror / border;处理 UV ∈ [0, 1] 之外的情况

      纹理单元的硬件优化
      • 缓存:相邻像素共享纹理缓存。

      • LOD 自动选择:基于屏幕空间梯度 |du/dx| + |dv/dx| + …​

      • 压缩格式:BC1-BC7 / ETC2 / ASTC;节省带宽 4-8 倍。

      • 数组纹理:texture array;同一 shader 可访问多张纹理。

      When:所有 3D 渲染(颜色 / 法线 / 材质参数);粒子精灵;UI 字体;后期处理。

      Example:纹理 256×256;UV (0.5, 0.5) 取 texel 中心;UV (0, 0) 取左下角(wrap 模式下 (1.5, 0.7) 取 (0.5, 0.7))。

      2.5 局部光照模型 (Standard Local Lighting Model)

      What:经验光照模型——环境光 + 漫反射 + 镜面反射;不依赖其他表面("局部")。

      Why:简单、便宜、视觉效果"够用";实时渲染主流;PBR 是更物理正确的替代。

      How

      Blinn-Phong 三部分:

      \[I_{\text{out}} = I_{\text{ambient}} + I_{\text{diffuse}} + I_{\text{specular}} \end{bmatrix}\]

      展开:

      \[I_{\text{ambient}} = k_a \cdot c_{\text{light}} \end{bmatrix}\]
      \[I_{\text{diffuse}} = k_d \cdot c_{\text{light}} \cdot \max(0, \mathbf{n} \cdot \mathbf{l}) \end{bmatrix}\]
      \[I_{\text{specular}} = k_s \cdot c_{\text{light}} \cdot \max(0, \mathbf{n} \cdot \mathbf{h})^p \end{bmatrix}\]

      其中 h = normalize(l + v)半向量p 是 shininess 指数。

      Phong vs Blinn-Phong
      • Phongpow(max(0, r · v), p),其中 r = reflect(-l, n);计算反射向量较贵。

      • Blinn-Phongpow(max(0, n · h), p),其中 h = normalize(l + v);不需反射向量,性能更优、效果接近。 现代 GPU 默认 Blinn-Phong。

      When:所有实时渲染;移动端 / 老硬件;非 PBR 项目。

      Example:太阳光(方向光)l = (0, 1, 0)、表面法向量 n = (0, 0, 1)n · l = 0 ⇒ 表面背向太阳,无光照。

      2.6 骨骼动画 (Skeletal Animation)

      What:用层级骨骼链驱动网格顶点位置;每顶点存骨骼索引 + 权重;GPU 蒙皮在 vertex shader 中完成。

      Why:角色动画的关键技术;用少量骨骼驱动大量顶点;内存 / 性能远胜逐顶点关键帧。

      How

      数据结构(§10.8):

      数据 内容

      骨骼层级

      每骨骼 4×4 变换矩阵 + 父骨骼索引

      顶点蒙皮

      每顶点 4 个骨骼索引 + 4 个权重(权重和 = 1)

      动画关键帧

      每骨骼的 SRT(缩放 / 旋转 / 平移)时间序列

      GPU 蒙皮(vertex shader):

      \[\mathbf{P}_{\text{skinned}} = \sum_{i=0}^{3} w_i \cdot (\mathbf{P}_{\text{bind}} \cdot \mathbf{B}_i) \end{bmatrix}\]

      其中 B_i 是骨骼 i 的世界变换矩阵、w_i 是权重。

      4 骨骼限制的原因

      GPU 顶点着色器的 uniform 寄存器数量有限;4 骨骼 / 顶点是大多数 GPU 的优化上限(可设 8 骨骼但寄存器压力更大)。多于 4 骨骼需 多 pass 蒙皮 或软件蒙皮。

      When:角色 / 生物 / 机械动画;面部表情(Blend Shape + 骨骼);任何"骨架驱动网格"的场景。

      Example:人形角色 50 根骨骼、5000 顶点;每顶点 4 权重;GPU 每帧 5000 顶点 × 4 骨骼 = 20000 次矩阵乘加 = 几毫秒。

      2.7 凹凸映射 (Bump Mapping)

      What:用纹理(法线贴图 / 视差贴图 / 位移贴图)模拟表面细节,不增加几何

      Why:节省内存 / 带宽 / 渲染时间;细节由 GPU 计算;劣势是无法改变 silhouette。

      How

      法线贴图(§10.9):

      • 纹理存"扰动法向量"(RGB ∈ [0, 1] 编码为切线空间向量 ∈ [-1, 1])。

      • vertex shader 输出切线空间基 (T, B, N)

      • fragment shader:N' = normalize(T · n.x + B · n.y + N · n.z),用 N' 替换 n 计算光照。

      视差贴图 / 步进视差:进一步用高度图模拟"深度位移"——UV 自适应偏移。

      位移贴图:真正位移顶点(tessellation / geometry shader),需要更多三角形。

      切线空间 = TBN 基

      法线贴图编码的是 切线空间 法向量;vertex shader 必须输出 (T, B, N) 三正交基(T = tangent、B = binormal、N = normal)。切线空间的好处:法线贴图与模型朝向无关——同一贴图可用于不同朝向的物体。

      When:所有 3D 渲染;墙面 / 砖块 / 皮肤毛孔 / 岩石细节;与 PBR 配合使用。

      Example:砖墙——基础立方体 + 砖块法线贴图;GPU 在 fragment 中按法线贴图扰动 n,产生"凹凸感"而无需细分几何。

      三、关键图表

      视觉图表

      图 10-1
      Figure 1. 图 10-1:图形管线总览
      图 10-2
      Figure 2. 图 10-2:相机坐标系约定
      图 10-3
      Figure 3. 图 10-3:视锥体(frustum)
      图 10-4
      Figure 4. 图 10-4:模型 / 视图 / 投影链
      图 10-5
      Figure 5. 图 10-5:索引三角网格 vs strip
      图 10-6
      Figure 6. 图 10-6:UV 映射(纹理坐标)
      图 10-7
      Figure 7. 图 10-7:Blinn-Phong 三部分(环境 + 漫反射 + 镜面)
      图 10-8
      Figure 8. 图 10-8:骨骼层级 + 蒙皮
      图 10-9
      Figure 9. 图 10-9:法线贴图 + 切线空间
      图 10-10
      Figure 10. 图 10-10:GPU 渲染管线(顶点 → 像素)

      非可视化条目

      非可视化条目(表 / 算法)
      编号 内容摘要

      表 10.1

      Blinn-Phong 三部分参数表(k_a, k_d, k_s, p)。

      式 10-1 至 10-12

      视图 / 投影矩阵构造;法线变换矩阵 = (M⁻¹)ᵀ;切线空间 TBN 构造。

      列表 10.1-10.x

      HLSL shader 示例:基本顶点变换、Phong 光照、纹理采样、骨骼蒙皮、法线贴图。

      表 10.2

      常用纹理压缩格式(BC1-BC7 / ETC2 / ASTC)的对比(内存 / 带宽 / 视觉质量)。

      表 10.3

      渲染方程(RTE)的物理量构成(BRDF / 入射辐射 / 几何项)。

      核心公式对照表

      核心公式对照表
      概念 公式

      模型-视图-投影

      \(\mathbf{P}_{\text{screen}} = \mathbf{P}_{\text{world}} \cdot \mathbf{M}_{\text{model}} \cdot \mathbf{M}_{\text{view}} \cdot \mathbf{M}_{\text{proj}}\)

      法线变换矩阵

      \(\mathbf{M}_{\text{normal}} = (\mathbf{M}^{-1})^T\)

      Blinn-Phong

      \(I = k_a c_L + k_d c_L \max(0, \mathbf{n} \cdot \mathbf{l}) + k_s c_L \max(0, \mathbf{n} \cdot \mathbf{h})^p\)

      半向量

      \(\mathbf{h} = \text{normalize}(\mathbf{l} + \mathbf{v})\)

      顶点蒙皮

      \(\mathbf{P}_{\text{skinned}} = \sum_{i=0}^{3} w_i (\mathbf{P}_{\text{bind}} \cdot \mathbf{B}_i)\)

      TBN 切线空间

      \(\mathbf{N}' = \mathbf{T} \cdot n_x + \mathbf{B} \cdot n_y + \mathbf{N} \cdot n_z\)(法线贴图扰动)

      四、思维导图

      mindmap
        root((第 10 章 3D 图形数学))
          图形管线
            顶点图元片段
            可编程shader
            每阶段数学
          3D视图
            模型视图投影链
            lookat构造
            视锥体
          网格
            索引三角
            strip压缩
            邻接查询
          纹理
            UV坐标
            mipmap过滤
            压缩格式
          光照
            BlinnPhong
            环境漫反射镜面
            半向量简化
          骨骼动画
            4骨骼权重
            GPU蒙皮
            uniform限制
          凹凸映射
            法线贴图
            TBN切线空间
            视差位移

      五、重点与易错点

      1. 图形管线每阶段都有数学:顶点变换、投影、光栅化插值、光照、纹理采样——定位性能瓶颈必先理解管线阶段。

      2. *模型矩阵 / 视图矩阵 / 投影矩阵*是 3 个独立概念:模型 = 物体→世界,视图 = 世界→相机,投影 = 相机→NDC。混用会导致"模型错位 / 相机错位 / 投影失真"。

      3. 法线变换矩阵 = (M⁻¹)ᵀ:直接用 model 矩阵变换法向量会因非均匀缩放而失真;必须用逆转置。

      4. 索引网格比直接三角省内存 3 倍以上:现代 GPU 顶点缓存依赖索引复用;导入模型务必索引化。

      5. Blinn-Phong 用半向量 h = normalize(l + v) 比 Phong 反射向量便宜:效果相近、GPU 友好。

      6. 光照 dot 积必须 max(0, …​):负值代表背面,应无光照;忘记 max(0) 会导致背光面反而"发光"。

      7. 4 骨骼蒙皮是 GPU uniform 寄存器上限:多于 4 骨骼需多 pass 蒙皮(VRAM → RAM → GPU 多次)或软件蒙皮(CPU 慢)。

      8. 法线贴图用切线空间(TBN):贴图与模型朝向无关,可复用;vertex shader 必须输出 TBN 给 fragment。

      9. mipmap 是必须的:远处不缩 mipmap 会闪烁 / 锯齿;硬件自动 LOD 选择基于屏幕空间梯度。

      10. PBR 是 Blinn-Phong 的物理升级:BRDF + 微表面 + 能量守恒;现代 AAA 默认使用(UE5 / Unity HDRP)。

      11. 跨章衔接:第 11 章力学用本节光照 / 蒙皮做物理可视化;第 12 章把 Blinn-Phong 推广到 PBR;第 13 章曲线用于角色手部 IK。