计算机图形学

《计算机图形学》全课程精讲

图形学不做几何本身,也不做光学本身,它做的是"骗过眼睛"这一件事——用数学把三维世界装进二维屏幕。
整门课只回答一个问题:一个三维点,最终怎么变成屏幕上某个像素的颜色?
如果你能把"渲染管线"每一步的输入、输出、公式、为什么这样设计都讲清楚,这门课就稳了。
以下是学长的考前串讲,按"一条主线 → 逐章拆解 → 全书收尾"展开,请配合教材与实验逐节吃透。


一、课程大纲

模块 章节 主要内容
基础 1. 图形学概述 应用领域、图形系统组成、图形API与OpenGL、渲染管线总览
基础 2. 数学基础 向量、点与坐标系、矩阵、齐次坐标的作用
几何 3. 二维几何变换与观察 平移/旋转/缩放、复合变换、齐次坐标、窗口→视区变换、直线/多边形裁剪
几何 4. 三维几何变换与三维观察 三维基本变换、观察坐标系、平行/透视投影、投影矩阵推导、深度缓冲概念
光栅化 5. 光栅化与扫描转换 DDA与Bresenham直线、Bresenham圆、扫描线多边形填充、边界标志、区域填充
光栅化 6. 隐藏面消除 画家算法、Z-buffer算法、扫描线消隐、BSP树与八叉树
着色 7. 光照与着色 环境光/漫反射/镜面反射、Phong与Blinn-Phong、Gouraud与Phong着色、法线变换
着色 8. 纹理映射 纹理坐标、双线性插值、环绕与过滤、凹凸映射
建模 9. 曲线与曲面 Bezier曲线、B样条、NURBS、曲面片与细分
实践 10. 现代OpenGL渲染管线 VAO/VBO/EBO、GLSL着色器、顶点/片元着色器、MVP矩阵、纹理与光照、工作流程示例
进阶 11. 真实感图形学进阶 光线追踪原理、辐射度、全局光照概念、反走样

复习权重建议:第3、4、7、10章为重中之重(出题概率极高,且实验必考);第5、6、8章为次重(算法流程常考简答/大题);第9章重概念与公式;第1、2、11章重概念与名词解释。


二、一条主线

把三维世界变成二维屏幕像素的过程。 整门课就是一条"图形渲染管线":

\[\boxed{\text{建模}} \to \boxed{\text{几何变换}} \to \boxed{\text{投影}} \to \boxed{\text{裁剪}} \to \boxed{\text{光栅化}} \to \boxed{\text{消隐}} \to \boxed{\text{着色}} \to \boxed{\text{纹理}} \to \boxed{\text{输出}} \]

每一个环节回答一个子问题,最终把"连续的世界"离散成"有限的像素":

  • 建模:世界里的物体用什么几何表示?(点、边、多边形面片、曲线曲面)
  • 几何变换:物体在哪儿、朝向哪?(模型、视图、投影的矩阵变换)
  • 投影:三维点怎么压到二维平面?(平行投影/透视投影)
  • 裁剪:屏幕外的东西如何高效扔掉?(Cohen-Sutherland、Liang-Barsky)
  • 光栅化:几何图形如何变成像素集合?(DDA、Bresenham、扫描线)
  • 消隐:被挡住的面怎么不画?(Z-buffer)
  • 着色:每个像素的颜色从哪来?(光照模型 + 纹理)

两个必须彻底吃透的核心概念:

  1. 光栅化(Rasterization):把连续的几何图形(直线、三角形面片)转换成分立像素的过程。本质是"离散化"。渲染管线的后半段全部围绕它展开——知道一个三角形的三个顶点,如何确定覆盖了哪些像素、每个像素在三角形内的哪个位置(重心坐标)。GPU 的核心工作就是三角形的光栅化。

  2. 采样(Sampling):在连续对象上按规律取点取值的过程。光栅化是对"位置"的采样,纹理映射是对"纹理值"的采样,着色是对"光能量"的采样。采样必然带来误差,最经典的后果就是走样(Aliasing)——锯齿、摩尔纹、小物体闪烁。整门课的很多技巧(反走样、超采样、纹理过滤)本质都在跟"采样信息不足"作斗争。一句话:连续的数学世界是理想,离散的像素是现实,图形学的所有算法都在弥合这条鸿沟。

所以图形学的本质是三个词:用数学(几何 + 光学)建模、用算法离散化、用硬件加速。记住这条主线,后面的每一章都是对管线上某一环节的深入。


三、逐章精讲

第1章 图形学概述

这一章在干什么

建立全局视角:图形学解决什么问题、系统由什么组成、我们通过什么接口去控制硬件。它是"管线的路线图",本身几乎不出难题,但名词概念密集。

核心内容

1. 应用领域

  • 计算机辅助设计/制造(CAD/CAM)、影视特效与动画(电影、CG)、游戏与虚拟现实(VR/AR)、科学计算可视化(医学影像、气象数据)、人机交互界面、数字图像处理(图像处理是"像素进、像素出",图形学是"描述进、像素出",注意区分)。

2. 图形系统的组成

  • 硬件:显示设备(CRT/LCD/LED,注意分辨率与刷新率)、图形输入设备(鼠标、键盘、数字化仪)、GPU 图形处理器(专门并行处理顶点与片元的硬件,现代图形系统的核心加速器)。
  • 软件:应用程序(调用API)→ 图形API(OpenGL、Direct3D、Vulkan)→ 图形驱动(把API指令翻译成GPU可执行的命令)→ 硬件。

3. 图形 API 与 OpenGL

  • 图形API是应用程序与GPU之间的"标准接口",让开发者不必关心具体硬件。
  • OpenGL 是一个跨平台、开放标准的状态机式 API:它内部保存大量状态(当前颜色、当前矩阵、当前着色器……),调用函数修改状态,然后绘制时按当前状态执行。现代 OpenGL(3.3+)强调可编程着色器(Shader),旧的固定管线已被弃用。
  • 其他 API:Direct3D(微软,Windows专有)、Vulkan(新一代高性能、显式控制)、WebGL(浏览器中的OpenGL ES)。

4. 渲染管线总览(第一次建立全貌)

管线分两大段:几何处理(顶点进来,坐标逐步变换到屏幕坐标,可编程部分为顶点着色器)和 片元处理(三角形覆盖像素,逐片元着色、深度测试、写入帧缓冲,可编程部分为片元着色器)。第10章会详细展开。

典型题型/考点

  • 名词解释:什么是图形API、什么是渲染管线、图形系统由哪几部分构成。
  • 区分:图像处理 vs 计算机图形学;矢量图形 vs 位图图形。

为什么这一章重要

它是后续所有章节的"目录"。理解管线总览后,第3、4章是它的前段(变换),第5、6章是它的中段(光栅化、消隐),第7、8章是它的后段(着色),第10章则用OpenGL把它们全部串起来。


第2章 数学基础

这一章在干什么

给整门课配齐"工具":用向量描述方向、用坐标描述位置、用矩阵描述变换。这一章看似简单,但所有变换公式都从这里长出,运算不熟后面寸步难行。

核心内容

1. 向量

  • 有大小有方向的量,几何上带箭头的线段。运算:加法(平行四边形法则)、数乘、点积、叉积。
  • 点积 \(\vec{a}\cdot\vec{b}=|\vec{a}||\vec{b}|\cos\theta = x_1x_2+y_1y_2+z_1z_2\)。用途:判断垂直(点积为0)、求投影长度、计算余弦(光照模型的核心)。
  • 叉积 \(\vec{a}\times\vec{b}\) 得到垂直于 \(\vec{a},\vec{b}\) 的向量,大小为 \(|\vec{a}||\vec{b}|\sin\theta\)。用途:求法向量、判断点在三角形内、判断两向量的相对转向。

2. 点与坐标系

  • 一个点由它在某坐标系下的坐标唯一确定;同一个点,在不同坐标系下坐标不同。变换的本质就是"坐标系的变换"。左手系/右手系影响叉积方向和旋转正方向,OpenGL 使用右手坐标系。

3. 矩阵

  • 矩阵乘法描述线性变换。注意矩阵乘法不满足交换律\(AB\neq BA\)。若先变换 \(A\) 再变换 \(B\),则复合矩阵写作 \(M=B\cdot A\)(列向量左乘,后写的先执行)。
  • 矩阵逆 \(M^{-1}\) 表示"撤销变换";正交矩阵 \(M^T=M^{-1}\)(旋转矩阵就是正交阵),其逆非常好求。

4. 齐次坐标——本章的灵魂

问题:平移不是线性变换,无法用 \(3\times3\) 矩阵表示(点 \((x,y,z)\) 加一个偏移量)。怎么办?

用四维表示三维点:三维点 \((x,y,z)\) 写成齐次坐标 \((x,y,z,1)\);方向向量写成 \((x,y,z,0)\)(末尾为0的向量平移不变)。这样平移、旋转、缩放都可以统一为 \(4\times4\) 矩阵乘法:

\[T=\begin{pmatrix}1&0&0&t_x\\0&1&0&t_y\\0&0&1&t_z\\0&0&0&1\end{pmatrix},\quad R_z(\theta)=\begin{pmatrix}\cos\theta&-\sin\theta&0&0\\\sin\theta&\cos\theta&0&0\\0&0&1&0\\0&0&0&1\end{pmatrix},\quad S=\begin{pmatrix}s_x&0&0&0\\0&s_y&0&0\\0&0&s_z&0\\0&0&0&1\end{pmatrix} \]

  • 齐次坐标的本质作用:让所有仿射变换统一为矩阵乘法,从而可以任意复合。从齐次坐标 \((x,y,z,w)\) 恢复普通坐标要除以 \(w\),这也为透视投影的"除法"埋下了伏笔(第4章)。

典型题型/考点

  • 给定点与变换序列,写出复合变换矩阵并计算新坐标(注意矩阵书写顺序)。
  • 旋转矩阵的推导:绕 \(x/y/z\) 轴旋转 \(\theta\)
  • 用齐次坐标表示点/向量,解释 \(w=1\)\(w=0\) 的区别。
  • 求变换矩阵的逆,如撤销旋转、反向平移。

为什么这一章重要

第3章二维变换、第4章三维变换、第10章 OpenGL 里 glm::translate/rotate/scale 与 MVP 矩阵,全部是本章概念的工程化。矩阵乘法顺序、齐次坐标这两个考点会一路考到期末。


第3章 二维几何变换与观察

这一章在干什么

先处理"二维世界里怎么摆物体、怎么取景"。虽然是二维,但它完整地演示了"变换 + 观察 + 裁剪"的完整思路,是三维章节的简化预演。

核心内容

1. 二维基本变换(齐次坐标形式)

  • 平移:\(T=\begin{pmatrix}1&0&t_x\\0&1&t_y\\0&0&1\end{pmatrix}\)
  • 缩放(绕原点):\(S=\begin{pmatrix}s_x&0&0\\0&s_y&0\\0&0&1\end{pmatrix}\)
  • 旋转(绕原点,逆时针为正):\(R(\theta)=\begin{pmatrix}\cos\theta&-\sin\theta&0\\\sin\theta&\cos\theta&0\\0&0&1\end{pmatrix}\)

2. 复合变换——变换的"烹饪法"

任意变换可拆成若干基本变换的乘积。三个必背套路:

  • 绕任意点 \(P=(x_0,y_0)\) 旋转:先平移到原点(\(T(-x_0,-y_0)\))→ 绕原点旋转(\(R\))→ 平移回去(\(T(x_0,y_0)\)),即 \(M=T(x_0,y_0)\cdot R\cdot T(-x_0,-y_0)\)
  • 绕任意点缩放:同上套路,中间换成 \(S\)
  • 关于任意直线的对称变换:先平移、再旋转使直线与坐标轴重合、做轴对称、逆序还原。公式记忆技巧:"先拉回原点/坐标轴,做完操作,再逆着还原",还原顺序与原顺序相反。

3. 窗口到视区变换(Window-to-Viewport)

  • 世界坐标系里取一个矩形"窗口"(看哪儿),屏幕设备坐标系里有一个矩形"视区"(画在哪儿)。变换即线性映射:

\[x_v = v_x + \frac{(x_w-x_{wmin})(v_xmax-v_x)}{w_xmax-w_x}\quad(同理 y) \]

  • 写成矩阵就是"平移 + 缩放 + 平移"的复合。直观理解:把窗口内容"搬"到视区并拉伸/压缩到合适大小。

4. 裁剪(Clipping)

只保留窗口内的部分,窗外的不画(省计算、防越界)。

  • Cohen-Sutherland(直线编码裁剪):把窗口四个边界外的区域编码成4位二进制码(上下右左,如 \(1001\)),用位与运算快速判断:
    • 两端点编码都为 \(0000\)完全可见,直接接受;
    • 两端点编码相与 \(\neq 0\)完全不可见(在同一侧外),直接丢弃;
    • 否则 → 求直线与窗口边界交点,裁剪掉窗外段,迭代处理。
    • 考点:会画编码表、会手工求交点。
  • Liang-Barsky(参数化裁剪):把线段写成参数式 \(P(t)=P_0+t(P_1-P_0),\,t\in[0,1]\),对每个窗口边界求参数区间,取交集的更新判据
    • \(p_i<0\):窗口外进内,取 \(t=u_1=\max(u_1, -q_i/p_i)\)
    • \(p_i>0\):窗口内出外,取 \(t=u_2=\min(u_2, -q_i/p_i)\)
    • \(p_i=0\)\(q_i<0\):线段与该边平行且在窗外,整体不可见。
    • \(u_1>u_2\) 则丢弃。它避免除法、计算量更小,是 Cohen-Sutherland 的改进。
  • 多边形裁剪常用 Sutherland-Hodgman(逐边裁剪,输出新多边形顶点序列,原理:一条裁剪边逐顶点扫描,在/出组合决定保留或求交点)。

典型题型/考点

  • 推导复合变换矩阵(绕任意点旋转/缩放),并求变换后坐标——必考大题
  • Cohen-Sutherland 编码并手动裁剪一条线段。
  • Liang-Barsky 参数 \(u_1,u_2\) 的迭代计算。
  • 窗口-视区变换公式的套用。

为什么这一章重要

它是第4章的二维版;裁剪算法思路(编码位运算、参数化)在三维视景体裁剪中同样适用;glViewport 就是窗口-视区变换的 API 实现,实验必涉及。


第4章 三维几何变换与三维观察

这一章在干什么

把二维的那套搬到三维:物体在三维世界怎么变换、相机怎么摆放、最终怎么"拍"到屏幕上。投影矩阵的推导是全课程含金量最高的计算题

核心内容

1. 三维基本变换

  • 平移、缩放与二维同理(加一个维度)。
  • 旋转:绕 \(x\) 轴:\(R_x=\begin{pmatrix}1&0&0&0\\0&\cos\theta&-\sin\theta&0\\0&\sin\theta&\cos\theta&0\\0&0&0&1\end{pmatrix}\);绕 \(y\) 轴注意 \(\sin\theta\) 符号与 \(x,z\) 轴不同(右手系绕 \(y\) 轴旋转,\(z\)\(x\));绕 \(z\) 轴与二维一致。
  • 绕任意轴旋转:把旋转轴平移过原点 → 旋转使其与某一坐标轴重合 → 绕该轴旋转 → 逆序还原(同二维套路,只是步骤更多)。

2. 观察坐标系(相机模型)

相机有三个属性:位置 \(P\)、观察方向(look-at 向量)、上方向 up。由此构建观察坐标系基:

  • 前向 \(\vec{f} = normalize(P_{目标}-P)\)(或反向)
  • 右向 \(\vec{r} = normalize(up \times \vec{f})\)(叉积)
  • 上向 \(\vec{u} = \vec{f}\times\vec{r}\)(再叉积保证正交)

再用这些基向量构造观察矩阵(View Matrix):把相机平移到原点、旋转使基与世界坐标轴对齐。OpenGL 里就是 glm::lookAt观察矩阵的作用:把世界坐标变成相机坐标。

3. 投影(Projection)——把三维压到二维

  • 平行投影(正交投影):投影线互相平行,物体大小不随距离变化(工程制图、CAD、游戏中的"2.5D"、阴影贴图)。正交投影矩阵把 \([l,r]\times[b,t]\times[n,f]\) 的视景体映射到标准立方体 \([-1,1]^3\)

\[M_{ortho}=\begin{pmatrix} \frac{2}{r-l}&0&0&-\frac{r+l}{r-l}\\ 0&\frac{2}{t-b}&0&-\frac{t+b}{t-b}\\ 0&0&-\frac{2}{f-n}&-\frac{f+n}{f-n}\\ 0&0&0&1 \end{pmatrix} \]

  • 透视投影:投影线汇聚于一点(视点),产生"近大远小"的深度感,符合人眼。核心:透视除法。设视点在原点、近平面 \(z=-n\)(OpenGL 里看向 \(-z\)),对点 \((x,y,z)\) 投影到近平面:

\[x' = \frac{n}{-z}\cdot x,\quad y'=\frac{n}{-z}\cdot y \]

这正是齐次坐标的用武之地:把 \(-z\) 放进 \(w\) 分量,利用"除以 \(w\)"完成透视除法。透视投影矩阵(frustum 形式)为:

\[M_{persp}=\begin{pmatrix} \frac{2n}{r-l}&0&\frac{r+l}{r-l}&0\\ 0&\frac{2n}{t-b}&\frac{t+b}{t-b}&0\\ 0&0&-\frac{f+n}{f-n}&-\frac{2fn}{f-n}\\ 0&0&-1&0 \end{pmatrix} \]

注意最后一列,它把 \(-z\) 写入 \(w\)所有顶点最终要执行"透视除法"(除以 \(w\),这一步在 GPU 上自动完成。坐标序:模型变换 \(M\) → 视图变换 \(V\) → 投影变换 \(P\),合称 MVP,顶点着色器里做 \(P\cdot V\cdot M\cdot p\),务必注意左乘顺序。

4. 深度缓冲(Z-buffer)概念引入

经过投影后,每个顶点的 \(z\)(线性化后)携带"深度"信息,在屏幕上它对应"离相机多远"。这正是第6章 Z-buffer 消隐的数据基础。第4章只需知道:三维观察最终把每个点映射到屏幕位置 + 深度值,深度决定遮挡关系。

典型题型/考点

  • 手推正交/透视投影矩阵;给一个三维点算投影后的屏幕坐标。
  • 用齐次坐标解释透视除法。
  • 求观察矩阵(给定相机位置、朝向)。
  • MVP 矩阵的乘法顺序与作用。

为什么这一章重要

投影矩阵推导是经典大题;观察/投影/视口是整个 3D 渲染的地基;第10章 OpenGL 中 glm::perspective, glm::lookAt 直接对应本章概念;深度值的引入直接衔接第6章。


第5章 光栅化与扫描转换

这一章在干什么

前面都是"连续的几何",这里开始离散化:把直线、圆、多边形变成像素。这是主线里"光栅化"环节的实体,算法思路(增量、误差控制)极具代表。

核心内容

1. 直线的 DDA 算法(Digital Differential Analyzer)

思路:按 \(x\)(或 \(y\))单位步进,用斜率增量式求另一坐标,四舍五入取像素。

  • 斜率 \(m=\Delta y/\Delta x\)。若 \(|m|\le 1\)\(x\) 每次加1,\(y_{k+1}=y_k+m\);否则对调 \(x,y\) 的角色。
  • 优点:直观、易实现;缺点:浮点运算 + 四舍五入,效率低。

2. 直线 Bresenham 算法——本章核心

思路:只用整数加、减、比较,用误差项判断"该取上面还是下面的像素"。推导要点:设直线斜率 \(0<m<1\),当前像素 \((x_k,y_k)\),下一列的两个候选是 \((x_k+1,y_k)\)\((x_k+1,y_k+1)\),比较它们与理想直线的误差,令判别式:

\[d_k = 2\Delta y \cdot x_k - 2\Delta x \cdot y_k + 2\Delta y - \Delta x \]

  • \(d_k<0\):取下方像素(\((x_k+1,y_k)\)),\(d_{k+1}=d_k+2\Delta y\)
  • \(d_k\ge0\):取上方像素(\((x_k+1,y_k+1)\)),\(d_{k+1}=d_k+2(\Delta y-\Delta x)\)

要点:判别式递推只用整数运算,这是它的最大优点。必须会手工对一条线段走几步,并注意 \(|m|>1\) 时交换 \(x,y\) 对称处理,以及斜率为负时的处理。

3. 圆的 Bresenham 算法

利用八分对称性,只画 \(1/8\) 圆。设圆心在原点、半径 \(R\),初始点 \((0,R)\),判别式 \(d=3-2R\)

  • \(d<0\)\(d_{k+1}=d_k+4x_k+6\),取 \((x_k+1,y_k)\)
  • 否则:\(d_{k+1}=d_k+4(x_k-y_k)+10\),取 \((x_k+1,y_k-1)\)

同样只有整数运算,且利用对称性大幅减少计算。

4. 多边形填充:扫描线算法(Seed/扫描线)

思路:用水平扫描线从上到下扫过多边形,求扫描线与多边形边界的交点,交点两两配对填色。

  • 求交点 → 交点排序 → 按奇偶规则配对成区间 → 填充区间内像素。
  • 边表(ET)+ 活动边表(AET):避免每条扫描线都求所有边的交点;AET 维护"当前扫描线相关的边",利用增量(\(\Delta x = 1/m\))递推交点。处理顶点时用"上开下闭"(取整规则:\(y_{max}\) 不计数)避免顶点重复配对。
  • 这是"离散化 + 有序化"的经典算法,大题常考。

5. 边界标志算法

对每条扫描线,先"描边"(标记边界像素),再沿扫描线从左到右用一个"在多边形内/外"的标志翻转填色。比扫描线算法简单,无需 ET/AET,适合硬件并行。

6. 区域填充(洪水填充/种子填充)

从内部种子像素出发,按 4-连通或 8-连通向相邻像素扩散,填满区域。4-连通填不满8-连通区域(对角会漏),注意区别。

典型题型/考点

  • 手算 Bresenham 直线/圆的前几个像素(必考)。
  • 扫描线填充:给多边形,画 ET/AET 过程,填色区间。
  • 概念辨析:DDA vs Bresenham 的优缺点;走样的原因。

为什么这一章重要

光栅化是 GPU 的看家本领,三角形光栅化的本质就是扫描线填充的并行版;Bresenham 的"整数增量思想"体现算法设计精髓;本章是"连续→离散"主线最直接的一环。


第6章 隐藏面消除

这一章在干什么

场景里很多面互相遮挡,只画"看得见"的面。消隐的本质是判定深度关系,其输出直接影响最终的像素写入。

核心内容

1. 画家算法(Painter's Algorithm)

先画远处的物体,再画近处的覆盖上去(像画家画油画)。要求:按深度对多边形排序(背面在前)。缺点:多边形互相穿插、循环遮挡时无法正确排序,需切分多边形,复杂且慢。优点是直观、零额外内存。

2. Z-buffer(深度缓冲)算法——本章核心

思路:每个像素存两个值——帧缓冲的颜色和深度缓冲的深度 \(z\)。逐个多边形光栅化,对每个片元:

  • 若片元深度 \(z_{new} < z_{buffer}\)(离相机更近):更新颜色与深度;
  • 否则丢弃。
for 每个多边形:
    for 多边形覆盖的每个像素:
        z = 该像素处多边形深度
        if z < zbuffer[像素]:
            zbuffer[像素] = z
            帧缓冲[像素] = 该处颜色
  • 优点:实现简单、与绘制顺序无关、支持任意多边形穿插,OpenGL 等所有实时 API 的默认方案
  • 缺点:需要深度缓冲内存;无法处理透明物体的半透明混合顺序。
  • 注意:深度精度问题(z 非线性分布,近处精度高远处精度低),常见解决方案是反向 z(Reversed-Z)与深度重映射。

3. 扫描线消隐

沿用扫描线思想:对每条扫描线,找出与它有交的所有多边形,按深度比较求出最可见的区间。一次扫描线同时完成光栅化与消隐,是旧硬件经典算法,现代 GPU 不再采用。

4. 空间分割:BSP 树与八叉树

  • BSP 树(二叉空间分割):用一个分割平面把空间分成前后两个半空间,递归分割直到每个子空间只有一个多边形。优点:能对场景排序,用于画家算法得到正确遮挡顺序,常用于静态场景。
  • 八叉树:把空间立方体递归八等分,物体只存储于其所在叶节点,用于加速射线求交、碰撞检测、视锥剔除(第11章光线追踪也用它加速)。注意 BSP 用于"排序",八叉树用于"加速查找"。

典型题型/考点

  • 手写 Z-buffer 算法流程(画表:像素的 zbuffer 值更新过程)。
  • 画家算法失效的例图。
  • 概念:Z-buffer 与画家算法比较;BSP 树构造。

为什么这一章重要

Z-buffer 是实时渲染的默认消隐,OpenGL 中 glEnable(GL_DEPTH_TEST) 就是开启它;深度缓冲与第4章的深度值、第10章的深度测试一脉相承;空间分割思想也为第11章的加速结构打基础。


第7章 光照与着色

这一章在干什么

解释"颜色从哪来"。一个表面被光照亮,反射进眼睛的光能量决定它的颜色。本章用局部光照模型近似物理光学,再讨论如何把光照算到每个像素上(着色策略)。

核心内容

1. 光照模型的三大分量

  • 环境光(Ambient):场景中散射、无处不在的背景光照近似,公式 \(I_a = k_a L_a\)。与位置、方向无关,只保证"暗处不全黑"。
  • 漫反射(Lambert):粗糙表面向各方向均匀反射,亮度只与入射角有关。朗伯余弦定律\(I_d = k_d L_d \max(\cos\theta,0)=k_d L_d \max(\vec{n}\cdot\vec{l},0)\),其中 \(\vec{n}\) 为法线、\(\vec{l}\) 为指向光源的单位向量。物体本身颜色由 \(k_d\)(漫反射系数)决定。
  • 镜面反射(Specular):光滑表面的高光,反射方向集中。Phong 模型:

\[I_s = k_s L_s (\vec{r}\cdot\vec{v})^n \]

\(\vec{r}\) 是反射方向,\(\vec{v}\) 是视线方向,\(n\)高光指数(越大高光越锐利)。反射向量 \(\vec{r}=2(\vec{n}\cdot\vec{l})\vec{n}-\vec{l}\)

  • Blinn-Phong 改进:用半程向量 \(\vec{h}=normalize(\vec{l}+\vec{v})\) 替代反射向量,\(I_s=k_s L_s(\vec{n}\cdot\vec{h})^n\)\(\vec{h}\) 只需求两向量之和再归一化,比求 \(\vec{r}\) 便宜,且结果更平滑,故实时渲染普遍采用。
  • 总光照:\(I = I_a+I_d+I_s\)(逐光源累加)。

2. 着色策略:Gouraud vs Phong

  • Gouraud 着色(顶点着色):在顶点处按光照公式算颜色,再在三角形面上线性插值颜色。快,但高光落在三角形内部时会被插值"抹平",出现马赫带、高光缺失。
  • Phong 着色(片元着色):在每个片元处对法线做插值,再逐片元算光照。质量高,高光正确,计算量大(现代 GPU 已完全负担得起,是默认做法)。
  • 记忆点:Gouraud 插值颜色,Phong 插值法线

3. 法线变换

模型变换包含缩放时,变换后的法线不能直接用顶点矩阵乘:若 \(M\) 含非均匀缩放,法线方向会错。正确做法是乘逆转置矩阵 \((\boldsymbol{M}^{-1})^T\)(逆矩阵的转置)。当 \(M\) 为正交阵(纯旋转)时,\((\boldsymbol{M}^{-1})^T=M\),可直接用。

典型题型/考点

  • 给定光源、法线、视线,用 Phong / Blinn-Phong 计算某点的颜色分量。
  • Gouraud vs Phong 区别(插值对象、高光效果)。
  • 法线为什么用逆转置矩阵(证明/解释)。

为什么这一章重要

这是"着色"环节的核心,是渲染出"真实感"的第一功臣;OpenGL 中固定光照已被弃用,自己写光照逻辑(Blinn-Phong)是第10章实验的经典题目;Blinn-Phong 至今仍是游戏渲染的主流基础模型。


第8章 纹理映射

这一章在干什么

给物体表面"贴图",让颜色在面上有细节变化,而不是一色到底。它是"像素颜色"在片元层面的采样:纹理 = 一张图,片元 = 一个采样点。

核心内容

1. 纹理坐标(UV)

  • 每个顶点带一个纹理坐标 \((u,v)\),取值范围 \([0,1]\)(归一化)。光栅化时,纹理坐标在三角形面上做重心坐标插值,得到每个片元的 \((u,v)\),再从纹理图取色。
  • 严格来说透视投影下应做透视校正插值\(u/z, v/z, 1/z\) 插值后再除),否则近处纹理会被拉伸、远处压缩——OpenGL 默认开启。

2. 纹理映射的采样过程

\(texel = Texture(floor(u\cdot W), floor(v\cdot H))\),对每个片元查纹理图中的纹素(texel)。这本质是对纹理函数的离散采样。

3. 双线性插值(Bilinear Filtering)

\((u,v)\) 不落在纹素中心时,取周围 4 个纹素,按距离做两次线性插值:

\[f(x,y) \approx (1-\Delta u)(1-\Delta v)f_{00} + \Delta u(1-\Delta v)f_{10} + (1-\Delta u)\Delta v f_{01} + \Delta u\Delta v f_{11} \]

作用:消除放大(Magnification)时的块状感

4. 环绕模式(Wrapping)

纹理坐标越界时的处理:重复(Repeat,取小数)镜像重复(Mirrored Repeat)钳位到边缘(Clamp to Edge)

5. 过滤(Filtering)

  • 放大(min 场景下叫缩小 Magnification/Minification)
    • 最近邻(Nearest):快,块状;
    • 双线性(Bilinear):平滑。
  • 缩小(Minification):多个纹素映射到一个像素,用单点采样会闪噪(摩尔纹)。解决:Mipmap——预先生成一串缩小一半的纹理金字塔,片元按所在层级就近采样,再做三线性插值(两次双线性 + 层级间线性插值)。Mipmap 的核心思想:预计算多分辨率,按需要选取,兼顾速度与质量。

6. 凹凸映射(Bump Mapping)简介

不改变几何,只"扰动法线",让平面看起来有凹凸。核心公式(扰动后法线):

\[\vec{n}' = normalize(\vec{n} + d_x\cdot \vec{t}_1 + d_y\cdot \vec{t}_2) \]

  • 法线贴图(Normal Mapping):直接存扰动后的法线。
  • 视差贴图、位移贴图是进一步改进(前者偏移 UV,后者真改几何)。

典型题型/考点

  • 双线性插值的手算(给四个纹素与插值位置算结果)。
  • UV 越界的环绕规则;放大/缩小分别用什么过滤、为什么。
  • Mipmap 的作用与层级选择。

为什么这一章重要

纹理让渲染从"塑料感"走向真实;OpenGL 中 glTexImage2DglTexParameteri(GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR) 就是本章概念;纹理采样与主线的"采样"思想呼应,是反走样的典型战场。


第9章 曲线与曲面

这一章在干什么

用少数控制点"捏出"光滑的曲线曲面,是建模环节的数学工具。核心思想:用参数方程 + 控制点加权,而不是逐点存储。

核心内容

1. Bezier 曲线

  • 参数形式:\(P(t)=\sum_{i=0}^{n} B_i^n(t)\,P_i,\ t\in[0,1]\),其中伯恩斯坦基函数:

\[B_i^n(t)=C_n^i\, t^i(1-t)^{n-i}=\frac{n!}{i!(n-i)!}t^i(1-t)^{n-i} \]

  • 三次 Bezier(\(n=3\)):\(P(t)=(1-t)^3P_0+3t(1-t)^2P_1+3t^2(1-t)P_2+t^3P_3\)性质:过首末控制点,与首末控制边相切;凸包性质(曲线在控制点凸包内);几何不变性(仿射变换后曲线不变形)。
  • de Casteljau 算法:递归线性插值 \(P_i^{k+1}(t)=(1-t)P_i^k+tP_{i+1}^k\),逐步细分得到曲线上的点。既用于求值,也用于画曲线。

2. B 样条曲线

针对 Bezier 的两个缺点(控制点数量决定多项式次数;移动一个点影响整条曲线)而生:

  • 节点向量(knot vector)分段,每段是低次多项式,曲线整体连续但每段只受附近控制点影响(局部性)。移动一个控制点只影响局部。
  • 均匀三次 B 样条基函数局部形式(常用的四段表达式,考试会给出)。Knot 均匀 vs 非均匀影响曲线性质。

3. NURBS 简介

非均匀有理 B 样条:在 B 样条上加权重(有理 = 齐次坐标的权重 \(w\)),能精确表示圆锥曲线(圆、椭圆)。是目前 CAD 行业标准(NURBS 建模)。

4. 曲面片(Bezier 曲面)与细分

  • Bezier 曲面:控制点网格 \(P_{ij}\),张量积 \(P(u,v)=\sum_i\sum_j B_i^n(u)B_j^m(v)P_{ij}\)。把 \(u\) 固定得一条 Bezier 曲线,再沿 \(v\) 插值。
  • 细分(Subdivision):把控制点网格不断加密细分,极限趋向光滑曲面(如 Catmull-Clark 细分)。游戏引擎中低模 + 细分/法线贴图是最常见的"廉价高模"手段。

典型题型/考点

  • 用 de Casteljau 或基函数公式求 Bezier 曲线上的点(手算三次曲线)。
  • Bezier 的性质判断题(端点、切向、凸包)。
  • B 样条与 Bezier 的对比;NURBS 的优势(圆锥曲线)。

为什么这一章重要

建模环节的数学核心;字体(TrueType 曲线)、工业设计、游戏曲面的基础;它与管线的衔接点在顶点数据——最终曲线曲面会被细分成三角形网格送入 GPU。


第10章 现代OpenGL渲染管线

这一章在干什么

把前面所有理论变成可运行的代码。现代 OpenGL(3.3+)用可编程着色器取代固定管线,你需要理解"数据怎么进 GPU、每阶段算什么"。这既是实验重头戏,也是期末重要考点。

核心内容

1. 现代 OpenGL 管线概览(与第1章呼应)

\[\text{顶点数据} \to \text{顶点着色器} \to \text{图元装配} \to \text{光栅化} \to \text{片元着色器} \to \text{逐片元操作(深度/模板测试)} \to \text{帧缓冲} \]

2. 缓冲对象:VBO / EBO / VAO(数据怎么送进 GPU)

  • VBO(顶点缓冲对象):把顶点属性数据(位置、法线、UV)存入 GPU 显存。创建流程:glGenBuffersglBindBuffer(GL_ARRAY_BUFFER, vbo)glBufferData 上传。
  • EBO(索引缓冲对象):存顶点索引,用索引绘制避免重复顶点(每个顶点只存一次),大幅省显存。注意绘制时用 glDrawElements
  • VAO(顶点数组对象)记录"VBO 里的数据如何解释"——即顶点属性指针的配置(glVertexAttribPointer 的布局)。绑定 VAO 后,所有属性配置一起生效,切换模型只需绑定不同 VAO。不绑定 VAO 画不出东西,这是新手最常见的坑。
// 配置流程(一次)
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(...);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 绘制流程(每帧)
glUseProgram(shader);
glBindVertexArray(vao);
glDrawArrays / glDrawElements;

3. 着色器语言 GLSL

  • 结构:以 #version 开头;layout(location=0) in vec3 aPos; 声明输入;out 传输出给下一阶段;uniform 是 CPU 传进来的全局值(每帧可改)。
  • 顶点着色器(逐顶点执行,输出裁剪空间坐标 gl_Position):
#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main() {
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}
  • 片元着色器(逐片元执行,输出颜色,从顶点着色器接收插值后的 varying 变量):
#version 330 core
out vec4 FragColor;
uniform vec3 lightColor;
void main() {
    FragColor = vec4(lightColor, 1.0);
}
  • 编译流程:编译每个 shader → glAttachShader → 链成 program → glUseProgram。调试:glGetShaderInfoLog / glGetProgramInfoLog

4. 变换矩阵与 MVP

  • glm 库:glm::translate/rotate/scaleglm::perspective(45.0f, aspect, 0.1f, 100.0f)glm::lookAt(...)
  • 把 MVP 矩阵通过 glUniformMatrix4fv 传入顶点着色器的 uniform mat4。注意 GLM 默认列主序,与 GLSL 匹配,直接 glm::value_ptr 传入。
  • 顺序:projection * view * model * 顶点(先模型后视图后投影)。

5. 纹理与光照在 OpenGL 中的实现

  • 纹理:glGenTexturesglBindTexture(GL_TEXTURE_2D, tex)glTexImage2D 上传 → glTexParameteri 设环绕/过滤(对应第8章)→ 用 glActiveTexture(GL_TEXTURE0) + glUniform1i 绑定到采样器 uniform sampler2D。片元着色器中 texture(tex, uv) 采样。
  • 光照(对应第7章):顶点/片元着色器中按 Blinn-Phong 计算;把 uniform vec3 lightPos, lightColor, viewPos 传入;法线经 glm::inverseTranspose(model) 变换(对应法线变换)。

6. 基本工作流程示例(最小可运行骨架)

  1. 初始化:glfwInit → 创建窗口/上下文 → gladLoadGL
  2. 准备数据:定义顶点/法线/UV → 填 VBO、EBO、VAO。
  3. 编译着色器 program。
  4. 设置矩阵 uniform 与纹理。
  5. 主循环:清屏(glClearColor + glClear)→ 设置 MVP → 绑定 VAO → glDrawElements → 交换缓冲(glfwSwapBuffers)→ 处理事件。
  6. 释放资源。

典型题型/考点

  • VAO/VBO/EBO 各自职责与绘制区别(glDrawArrays vs glDrawElements)。
  • 顶点着色器 vs 片元着色器的输入输出与执行频率。
  • 写一段 GLSL:MVP 变换、Phong 光照、纹理采样。
  • MVP 矩阵在 CPU 侧的计算与传入方式。
  • 描述渲染一帧三角形的完整调用序列。

为什么这一章重要

它是全课程的"集成实验":把变换(4章)、光栅化(5章)、深度测试(6章)、光照(7章)、纹理(8章)全部编译进一段代码。会写这一章,就证明你真正理解了整条管线。游戏引擎(Unity/UE)底层正是如此,工程岗位面试常考。


第11章 真实感图形学进阶

这一章在干什么

局部光照(第7章)只管"一束光的直达",无法表现全局效果:阴影、反射、焦散、间接光。本章介绍更真实的算法方向(全局光照)与走样问题(反走样),属于概念扩展章。

核心内容

1. 光线追踪(Ray Tracing)原理

从眼睛出发向每个像素发射光线,追踪其在场景中的反射/折射/求交过程,倒推光路。核心步骤:

  1. 对每个像素发射主光线(Eye→像素);
  2. 求光线与场景所有物体的最近交点;
  3. 在交点处,计算到光源的阴影光线(被挡则阴影)、反射光线、折射光线;
  4. 递归累加光照,得像素颜色。
  • 优点:真实感强(反射、阴影、透明一次搞定),与渲染顺序无关;
  • 缺点:逐像素逐交点计算量极大,实时性差(电影用,游戏难用)。
  • 加速手段:包围盒、八叉树、BVH(层次包围盒)。近年来 RTX / 光追硬件让实时光追成为现实(配合降噪)。

2. 辐射度(Radiosity)简介

  • 把场景离散成小面元,面元之间互相传递能量,迭代求解稳态能量分布,得到间接光照。适合表现漫反射表面的柔和明暗,但与视线无关、不支持高光,计算量大。

3. 全局光照(GI)概念

同时考虑直接光与间接光(光线在物体间多次弹射)。典型算法:路径追踪(Path Tracing)——每像素发射多根光线随机弹射做蒙特卡洛估计,是电影级渲染标准(与第7章局部光照的"只算直接光"形成对比)。实时 GI 用光照贴图(Lightmap,预计算)、反射探针、环境光遮蔽(AO) 等近似。

4. 反走样(Anti-aliasing,AA)

走样的根源:采样率不足(主线的"采样")。锯齿是几何边缘的走样,摩尔纹是纹理的走样。

  • 超采样抗锯齿 SSAA:把像素细分后采样再平均,质量最好,代价最大。
  • 多重采样 MSAA:只对像素覆盖子样本点做深度/覆盖测试,片元着色器仍每个像素跑一次,性价比高,GPU 常见。
  • FXAA:后处理,直接在图像上找边缘模糊,快但略糊。
  • 时间性抗锯齿 TAA:利用历史帧信息抖动采样。
  • 纹理层面:Mipmap + 各向异性过滤(第8章)。

典型题型/考点

  • 手写/口述光线追踪主循环步骤。
  • 光追 vs 光栅化优缺点对比;实时 vs 离线的取舍。
  • 名词解释:全局光照、辐射度、路径追踪、SSAA/MSAA/FXAA。
  • 走样的原因与对策。

为什么这一章重要

它补全了"真实感"的最后一环,是当前图形学最热方向(光追、DLSS、NeRF 渲染的根基);同时把第5章的采样思想、第8章的纹理走样收束到"反走样"这一统摄概念上,是复习主线的完美落点。


四、全书收尾

渲染管线全景图(把整门课画在一张图里):

        连续的世界                       离散的像素
┌────────────────────────────────────────────────────────────────────────────┐
│                                                                              │
│  【第9章 建模】      控制点/曲面 ──细分──▶ 三角形网格(顶点+法线+UV)          │
│     曲线曲面               │                                                │
│                            ▼                                                │
│  【第2、3、4章 变换】   局部坐标 ─模型矩阵M─▶ 世界坐标 ─视图矩阵V─▶ 相机坐标    │
│     向量/矩阵/齐次      ─投影矩阵P─▶ 裁剪空间 ─透视除法/视口─▶ 屏幕坐标+深度    │
│                            │                                                │
│  【第3章 裁剪】        ▼ Cohen-Sutherland / Liang-Barsky 剪掉窗外部分          │
│                            │                                                │
│  【第5章 光栅化】      ▼ DDA / Bresenham / 扫描线  ──▶ 覆盖哪些像素?         │
│                            │(对三角形:重心坐标插值 位置/UV/法线/深度)        │
│                            ▼                                                │
│  【第6章 消隐】        ▼ Z-buffer 深度测试  ──▶ 谁可见?                      │
│                            ▼                                                │
│  【第7章 着色】        ▼ 光照模型(环境+漫反射+Blinn-Phong高光) ──▶ 颜色        │
│                            │                                                │
│  【第8章 纹理】        ▼ UV映射+双线性插值+Mipmap ──▶ 表面细节               │
│                            │                                                │
│  【第10章 OpenGL】     ▼ VAO/VBO/EBO 送数据 → 顶点着色器 → 光栅化             │
│                          → 片元着色器 → 深度测试 → 帧缓冲 → 屏幕              │
│                            │                                                │
│  【第11章 进阶】       ▼ 光线追踪/全局光照(真实感的更高追求)                │
│                          ▼ 反走样(对抗"采样不足")                          │
│                            │                                                │
└────────────────────────────────────────────────────────────────────────────┘

几句话总结:

整门课只有一条逻辑:建模(9)→ 变换(2、3、4)→ 裁剪(3)→ 光栅化(5)→ 消隐(6)→ 着色(7、8)→ 用 OpenGL 落成代码(10)→ 向真实感进发(11)。 每一步都是"把连续世界往离散像素上搬",且每一步都在用"矩阵"与"算法"对抗"信息损失"——这是你贯穿始终的主线记忆锚点。

复习权重(给到具体)

  • 必考大题:复合变换矩阵推导(3)、投影矩阵推导(4)、Bresenham 直线/圆手算(5)、扫描线填充(5)、Z-buffer 流程表(6)、Phong/Blinn-Phong 光照计算(7)、OpenGL 绘制流程 + GLSL 写段代码(10)。
  • 常考简答/名词:齐次坐标作用(2)、Cohen-Sutherland 编码(3)、Gouraud vs Phong(7)、双线性插值与 Mipmap(8)、Bezier 性质(9)、VAO/VBO/EBO(10)、光追 vs 光栅化、反走样(11)。
  • 时间分配建议:第4、10章各占约 25% 复习时间(计算+代码),第3、5、6、7章各约 10%,其余章节以概念通读为主。

祝考试顺利——把这条管线的每一站讲清楚,你就是最稳的那个。

posted @ 2026-09-04 16:52  IcarusLee  阅读(7)  评论(0)    收藏  举报