需要预先学习:


1. 着色(Shading)

着色(shading)指对物体表面应用材质(material)的过程——计算表面每个点呈现的颜色。在渲染管线中,光栅化产生片元之后,由着色阶段决定片元的颜色。

着色的输入包括:光源的位置与强度、观察方向、表面法线(normal,表面在该点的朝向)、材质属性(颜色、光泽度等)。

着色是局部的:每个点独立计算颜色,只考虑光源与表面本身,不考虑其他物体。物体之间的遮挡(阴影)与光线的多次反弹属于全局光照(global illumination),由光线追踪等更复杂的算法处理,不在本节范围。


2. Blinn-Phong 反射模型

Blinn-Phong 反射模型是最常用的局部光照模型(local illumination model)之一,它把表面颜色分解为三部分之和:环境光(ambient)、漫反射(diffuse)、高光(specular)。


2.1 基本约定

在着色点(shading point,表面上的一个点)处定义以下向量:

符号 含义
n\mathbf{n} 表面法线,单位向量
l\mathbf{l} 指向光源的方向,单位向量
v\mathbf{v} 指向观察者(摄像机)的方向,单位向量
rr 光源到着色点的距离

2.2 漫反射

漫反射(diffuse reflection):光线打到粗糙表面后向各个方向均匀散射,因此漫反射颜色与观察方向无关——从任何角度看都一样。

接收到的光量由 Lambert 余弦定律(Lambert’s cosine law)决定:光倾斜照射时,单位面积接收的能量随夹角余弦衰减,即正比于 n⋅l\mathbf{n} \cdot \mathbf{l}。

同时,点光源向四面八方辐射,能量均匀分布在半径 rr 的球面上,能量密度与距离平方成反比(Ir2\frac{I}{r^2},II 为光源强度)。合并得到漫反射项:

Ld=kdIr2max⁡(0,n⋅l)L_d = k_d \frac{I}{r^2} \max(0, \mathbf{n} \cdot \mathbf{l})

其中 kdk_d 是漫反射系数(物体的固有色),max⁡(0,⋅)\max(0, \cdot) 保证背面(法线与光方向夹角超过 90°)不被照亮。


2.3 高光

高光(specular highlight):光滑表面把光线集中反射到镜像方向附近,观察方向越接近镜像方向,看到的高光越亮。

Phong 模型用反射向量 r\mathbf{r} 与观察方向 v\mathbf{v} 的夹角衡量;Blinn-Phong 模型改用半程向量(halfway vector)——l\mathbf{l} 与 v\mathbf{v} 的角平分线方向:

h=l+v∣l+v∣\mathbf{h} = \frac{\mathbf{l} + \mathbf{v}}{|\mathbf{l} + \mathbf{v}|}

当法线与半程向量对齐(n⋅h\mathbf{n} \cdot \mathbf{h} 接近 1)时,观察方向恰好等于光的镜像方向。高光项为:

Ls=ksIr2max⁡(0,n⋅h)pL_s = k_s \frac{I}{r^2} \max(0, \mathbf{n} \cdot \mathbf{h})^p

ksk_s 是高光系数,pp 是高光指数(shininess),控制高光的大小:pp 越大,高光越集中、越小,材质显得越光滑。

注:Blinn-Phong 用半程向量 h\mathbf{h} 替代 Phong 模型中的反射向量 r\mathbf{r},省去反射向量的计算,速度更快且效果几乎一致,因此被广泛使用。


2.4 环境光

环境光(ambient)是间接光照的粗糙近似:光线在场景中多次反弹后近似均匀地照到物体上,与方向无关,用一个常数表示:

La=kaIaL_a = k_a I_a

其中 kak_a 是环境光系数,IaI_a 是环境光强度。


2.5 整合

把三项相加,得到 Blinn-Phong 模型的输出颜色:

L=La+Ld+Ls=kaIa+kdIr2max⁡(0,n⋅l)+ksIr2max⁡(0,n⋅h)pL = L_a + L_d + L_s = k_a I_a + k_d \frac{I}{r^2} \max(0, \mathbf{n} \cdot \mathbf{l}) + k_s \frac{I}{r^2} \max(0, \mathbf{n} \cdot \mathbf{h})^p

参数 含义
kak_a 环境光系数,近似间接光
kdk_d 漫反射系数,物体固有色
ksk_s 高光系数,高光强度
pp 高光指数,越大高光越集中
II、IaI_a 光源强度、环境光强度
rr 光源到着色点的距离
// 伪代码:Blinn-Phong 着色
Vec3 blinn_phong(const Vec3& n, const Vec3& l, const Vec3& v, float r)
{
    Vec3 h = normalize(l + v);                            // 半程向量
    float diffuse  = k_d * (I / (r * r)) * max(0.0f, dot(n, l));
    float specular = k_s * (I / (r * r)) * pow(max(0.0f, dot(n, h)), p);
    float ambient  = k_a * I_a;
    return ambient + diffuse + specular;
}

3. 渲染管线

渲染管线全流程思维导图


3.1 顶点处理(Vertex Processing)

输入:模型空间中的三维顶点及其属性(位置、法线、颜色等)。

处理,依次经过:

  1. 模型变换(变换):模型空间 → 世界空间,摆放物体;
  2. 视图变换(视图(摄像机)与投影):世界空间 → 摄像机空间;
  3. 投影变换:摄像机空间 → NDC 标准立方体;
  4. 裁剪(clipping):剔除完全在可视区域外的几何,并裁剪与边界相交的部分;
  5. 视口变换(光栅化):NDC → 屏幕坐标。

输出:屏幕空间中的顶点(位置、深度 z 与供插值使用的属性)。


3.2 三角形处理(Triangle Processing)

输入:屏幕空间顶点。

处理:把顶点组装成三角形图元,这一步也称图元装配(primitive assembly)或 triangle setup,并为光栅化准备三角形信息(边方程等)。

输出:完整的三角形。


3.3 光栅化(Rasterization)

输入:三角形。

处理(见 光栅化):

  • 采样:判断像素中心是否在三角形内(叉积法 + 包围盒加速);
  • 插值:用重心坐标在三个顶点之间插值颜色、法线等属性;
  • 生成片元:每个被覆盖的像素产生一个片元(fragment)。

输出:片元,携带屏幕位置、深度与插值后的属性。


3.4 片元处理(Fragment Processing)

输入:片元。

处理:

  1. 着色:用光照模型(如 Blinn-Phong)计算片元颜色;
  2. 模板测试(stencil test):按模板缓冲(stencil buffer)的内容裁剪片元,用于描边、遮罩等效果;
  3. 深度测试(depth test):与深度缓冲比较,不通过则丢弃(见 深度缓冲);
  4. 混合(blending):与帧缓冲中已有的颜色混合,实现半透明效果。

输出:通过所有测试的片元更新帧缓冲与深度缓冲。

注:模板测试或深度测试失败的片元直接被丢弃,不会写入帧缓冲;混合发生在片元写入帧缓冲之前。


3.5 帧缓冲区操作(Framebuffer Operation)

输入:帧缓冲(颜色)以及深度缓冲、模板缓冲中的数据。

处理:把帧缓冲的内容呈现到屏幕上。实际硬件中常用双缓冲(double buffering):渲染写入后台缓冲、显示读取前台缓冲,一帧完成后交换(swap)二者,避免画面撕裂(tearing)。

输出:屏幕上的最终图像。

至此,一条完整的渲染管线闭环了:顶点 → 三角形 → 片元 → 颜色 → 图像。