着色与渲染管线
需要预先学习:
1. 着色(Shading)
着色(shading)指对物体表面应用材质(material)的过程——计算表面每个点呈现的颜色。在渲染管线中,光栅化产生片元之后,由着色阶段决定片元的颜色。
着色的输入包括:光源的位置与强度、观察方向、表面法线(normal,表面在该点的朝向)、材质属性(颜色、光泽度等)。
着色是局部的:每个点独立计算颜色,只考虑光源与表面本身,不考虑其他物体。物体之间的遮挡(阴影)与光线的多次反弹属于全局光照(global illumination),由光线追踪等更复杂的算法处理,不在本节范围。
2. Blinn-Phong 反射模型
Blinn-Phong 反射模型是最常用的局部光照模型(local illumination model)之一,它把表面颜色分解为三部分之和:环境光(ambient)、漫反射(diffuse)、高光(specular)。
2.1 基本约定
在着色点(shading point,表面上的一个点)处定义以下向量:
| 符号 | 含义 |
|---|---|
| 表面法线,单位向量 | |
| 指向光源的方向,单位向量 | |
| 指向观察者(摄像机)的方向,单位向量 | |
| 光源到着色点的距离 |
2.2 漫反射
漫反射(diffuse reflection):光线打到粗糙表面后向各个方向均匀散射,因此漫反射颜色与观察方向无关——从任何角度看都一样。
接收到的光量由 Lambert 余弦定律(Lambert’s cosine law)决定:光倾斜照射时,单位面积接收的能量随夹角余弦衰减,即正比于 。
同时,点光源向四面八方辐射,能量均匀分布在半径 的球面上,能量密度与距离平方成反比(, 为光源强度)。合并得到漫反射项:
其中 是漫反射系数(物体的固有色), 保证背面(法线与光方向夹角超过 90°)不被照亮。
2.3 高光
高光(specular highlight):光滑表面把光线集中反射到镜像方向附近,观察方向越接近镜像方向,看到的高光越亮。
Phong 模型用反射向量 与观察方向 的夹角衡量;Blinn-Phong 模型改用半程向量(halfway vector)—— 与 的角平分线方向:
当法线与半程向量对齐( 接近 1)时,观察方向恰好等于光的镜像方向。高光项为:
是高光系数, 是高光指数(shininess),控制高光的大小: 越大,高光越集中、越小,材质显得越光滑。
注:Blinn-Phong 用半程向量 替代 Phong 模型中的反射向量 ,省去反射向量的计算,速度更快且效果几乎一致,因此被广泛使用。
2.4 环境光
环境光(ambient)是间接光照的粗糙近似:光线在场景中多次反弹后近似均匀地照到物体上,与方向无关,用一个常数表示:
其中 是环境光系数, 是环境光强度。
2.5 整合
把三项相加,得到 Blinn-Phong 模型的输出颜色:
| 参数 | 含义 |
|---|---|
| 环境光系数,近似间接光 | |
| 漫反射系数,物体固有色 | |
| 高光系数,高光强度 | |
| 高光指数,越大高光越集中 | |
| 、 | 光源强度、环境光强度 |
| 光源到着色点的距离 |
// 伪代码: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)
输入:模型空间中的三维顶点及其属性(位置、法线、颜色等)。
处理,依次经过:
- 模型变换(变换):模型空间 → 世界空间,摆放物体;
- 视图变换(视图(摄像机)与投影):世界空间 → 摄像机空间;
- 投影变换:摄像机空间 → NDC 标准立方体;
- 裁剪(clipping):剔除完全在可视区域外的几何,并裁剪与边界相交的部分;
- 视口变换(光栅化):NDC → 屏幕坐标。
输出:屏幕空间中的顶点(位置、深度 z 与供插值使用的属性)。
3.2 三角形处理(Triangle Processing)
输入:屏幕空间顶点。
处理:把顶点组装成三角形图元,这一步也称图元装配(primitive assembly)或 triangle setup,并为光栅化准备三角形信息(边方程等)。
输出:完整的三角形。
3.3 光栅化(Rasterization)
输入:三角形。
处理(见 光栅化):
- 采样:判断像素中心是否在三角形内(叉积法 + 包围盒加速);
- 插值:用重心坐标在三个顶点之间插值颜色、法线等属性;
- 生成片元:每个被覆盖的像素产生一个片元(fragment)。
输出:片元,携带屏幕位置、深度与插值后的属性。
3.4 片元处理(Fragment Processing)
输入:片元。
处理:
- 着色:用光照模型(如 Blinn-Phong)计算片元颜色;
- 模板测试(stencil test):按模板缓冲(stencil buffer)的内容裁剪片元,用于描边、遮罩等效果;
- 深度测试(depth test):与深度缓冲比较,不通过则丢弃(见 深度缓冲);
- 混合(blending):与帧缓冲中已有的颜色混合,实现半透明效果。
输出:通过所有测试的片元更新帧缓冲与深度缓冲。
注:模板测试或深度测试失败的片元直接被丢弃,不会写入帧缓冲;混合发生在片元写入帧缓冲之前。
3.5 帧缓冲区操作(Framebuffer Operation)
输入:帧缓冲(颜色)以及深度缓冲、模板缓冲中的数据。
处理:把帧缓冲的内容呈现到屏幕上。实际硬件中常用双缓冲(double buffering):渲染写入后台缓冲、显示读取前台缓冲,一帧完成后交换(swap)二者,避免画面撕裂(tearing)。
输出:屏幕上的最终图像。
至此,一条完整的渲染管线闭环了:顶点 → 三角形 → 片元 → 颜色 → 图像。


