VCI - 13: 着色 (Shading)
课程: 北京大学视觉计算 (Visual Computing) 2025秋季 章节: 第13章 着色 内容: 光源、反射模型、照明方程、着色算法、可见性、阴影、非照相写实渲染
目录
1. 着色概述
1.1 什么是着色
着色 (Shading) 是对几何体进行着色,使其看起来像具有特定材质性质并在光照下反应的物体。
1.2 着色的三个关键要素
- 光传输 (Light Transport) — 光线如何从光源传播到表面
- 反射率 (Reflectance) — 材料表面如何反射光
- 感知 (Perception) — 人眼如何感知最终的图像
1.3 光栅化vs光线追踪
| 特性 | 光栅化 (Rasterization) | 光线追踪 (Ray Tracing) |
|---|---|---|
| 类比 | 绘画 — 逐一着色物体 | 摄影 — 追踪光线 |
| 方法 | 经验光照模型 | 物理光线交互 |
| 效率 | 高效 | 低效 |
| 真实性 | 较低 | 逼真 |
| 应用 | 实时渲染 | 离线渲染 |
2. 光源
2.1 环境光 (Ambient Light)
定义:全方向照射的光,强度相同
特点:
- 是一种hack,用来模拟多次反射产生的背景光
- 没有方向
- 均匀照亮场景
数学表示:
\[I = k_a I_a\]其中 $k_a$ 是环境反射率,$I_a$ 是环境光强度。
2.2 平行光 (Directional Light)
定义:光线方向相同的光源
应用场景:
- 太阳光 — 距离很远,可以近似为平行光
- 舞台灯光
特点:
- 没有衰减
- 光线方向已知且恒定
- 适合远距离光源
2.3 点光源 (Point Light)
定义:从单一点发出的光线向四周散发
应用场景:
- 灯泡
- 火焰
特点:
- 有衰减(反平方律)
- 光线方向随位置变化
衰减函数:
\[f_{att} = \frac{1}{d^2}\]其中 $d$ 是光源到表面的距离。
2.4 聚光灯 (Spotlight)
定义:点光源加上方向性的衰减
参数:
- 位置
- 主方向 $\vec{D}$
- 强度衰减参数
2.5 面光源 (Area Light)
定义:发光的2D表面
特点:
- 产生软阴影(阴影边界模糊)
- 计算复杂
实现方式:
- 分解为多个点光源
- 使用重要性采样
3. 反射与照明模型
3.1 漫反射 (Diffuse Reflection)
Lambert定律
漫反射材料(哑光表面)的反射强度由表面法线和光线方向决定:
\[I_d = k_d I_l \cos\theta = k_d I_l (\vec{n} \cdot \vec{l})\]参数:
- $I_d$ — 漫反射强度
- $k_d$ — 漫反射系数 ($0 \leq k_d \leq 1$)
- $I_l$ — 光源强度
- $\theta$ — 法线与光线的夹角
特点:
- 从任何观察方向看亮度相同
- 亮度只取决于入射光方向和表面法线
- 常见于非光泽表面(纸张、布料、砖墙等)
环境光 + 漫反射
结合环境光项以避免完全黑色的阴影:
\[I_{a+d} = k_a I_a + k_d I_l \cos\theta\]这是计算机图形学早期的标准做法,虽然物理上不准确,但在实时渲染中仍然广泛使用。
3.2 镜面反射 (Specular Reflection)
定义:光线在微观光滑的表面上形成的高光
特点:
- 观察角度相关 — 高光只在特定观察角度可见
- 由光线和法线决定的反射方向
- 常见于光泽表面(金属、抛光物体)
计算反射方向
给定入射光方向 $\vec{l}$ 和表面法线 $\vec{n}$,反射方向 $\vec{r}$:
\[\vec{r} = 2(\vec{n} \cdot \vec{l})\vec{n} - \vec{l}\]Phong照明模型
Phong模型用于近似镜面反射的衰减:
\[I_s = k_s I_l \cos^{n_s}\phi\]参数:
- $\phi$ — 反射光线与观察方向的夹角
- $k_s$ — 镜面反射系数
- $n_s$ — 镜面指数(控制高光锐利程度)
特点:
- $n_s$ 越大,高光越尖锐(更光滑的表面)
- $n_s$ 越小,高光越宽(更粗糙的表面)
- 没有物理基础,但在图形学中广泛使用
- 值通常在 100-200 之间
3.3 完整的Phong照明方程
结合环境光、漫反射和镜面反射:
\[I = k_a I_a + f_{att} I_l [k_d \max(0, \cos\theta) + k_s \cos^{n_s}\phi]\]对多个光源:
- 对每个光源重复计算漫反射和镜面反射项
- 将所有光源的贡献求和
- 环境光项只计算一次
材料特性的表达:
- 简单金属 — $k_a$ 和 $k_d$ 有材料颜色,$k_s$ 为白色
- 简单塑料 — $k_s$ 也包含材料颜色
4. 着色算法
着色的关键问题:在表面的哪些点计算照明?这直接影响渲染效果。
4.1 平面着色 (Flat Shading)
方法:
- 计算每个基元(三角形)的一个照明值
- 通常在重心处计算
优点:
- 快速
- 简单
缺点:
- 明显的面段效应 — 多面体的面段清晰可见
- 对于曲面外观不佳
4.2 Gouraud着色
方法:
- 在每个顶点计算照明值
- 在三角形内部线性插值颜色
优点:
- 比平面着色更光滑
- 仍然相对快速
缺点:
- 屏幕空间插值错误(应该在世界空间插值)
- 高光可能被”吞掉”(高光位于三角形内部时)
- 在不同细节级别间切换时会产生跳跃
4.3 Phong着色
方法:
- 在每个顶点存储法线
- 在三角形内部插值法线(世界/模型空间)
- 在每个像素点计算照明(使用插值的法线)
优点:
- 非常光滑的外观
- 高光更准确
- 只在剪影边缘能看到多面体效应
特点:
- 比Gouraud着色计算量大(需要每像素着色)
- 现代GPU非常高效,通常是默认选择
4.4 比较
| 方法 | 计算点数 | 视觉质量 | 速度 |
|---|---|---|---|
| 平面着色 | 每基元1点 | 差 | 最快 |
| Gouraud着色 | 每顶点1点 | 中 | 快 |
| Phong着色 | 每像素1点 | 好 | 较慢 |
5. 可见性问题
5.1 问题定义
可见性问题 (Visibility Problem):给定一个像素位置,哪个表面离观察者最近?
这是渲染中最基本的问题之一。
5.2 画家算法 (Painter’s Algorithm)
原理:像油画一样,从后往前绘制物体
步骤:
- 按深度排序对象(后到前)
- 按此顺序投影和光栅化
- 前面的物体会覆盖后面的
优点:
- 概念简单
- 处理透明度良好
缺点:
- 排序可能昂贵(不是线性时间)
- 遇到循环遮挡时需要分割对象
- 对非多边形物体困难
问题示例:
两个对象A和B的深度关系可能是:
- A遮挡B (zmin(A) < zmin(B))
- B遮挡A (完全相反)
- 循环遮挡 (A部分遮挡B,B部分遮挡A)
5.3 Z-缓冲算法 (Z-Buffer Algorithm)
原理:维护每个像素的最小深度,只绘制更近的物体
算法:
// 初始化
for each (x, y):
zbuf[x,y] = infinity
// 绘制
for each object:
for each pixel (x,y) in object:
z = compute_depth(x, y)
if z < zbuf[x,y]:
zbuf[x,y] = z
image[x,y] = shade(x, y)
优点:
- 简单,无需排序或分割
- 易于混合不同几何体类型(多边形、球体等)
- 标准硬件支持
缺点:
- 无法处理透明度(需要额外的技巧)
- Z-缓冲分辨率不足会产生深度排序伪影
- 需要浮点精度(防止Z-fighting)
5.4 后向面剔除 (Back-Face Culling)
原理:如果多边形法线指向远离观察者,则不渲染它
判断方法:
\[\vec{v} \cdot \vec{n} \geq 0 \Rightarrow \text{back-facing}\]其中 $\vec{v}$ 是观察方向,$\vec{n}$ 是表面法线。
优点:
- 可以消除大约一半的三角形
- 对闭合物体有效
限制:
- 仅对凸物体或闭合网格有效
- 非闭合表面可能产生错误
5.5 复杂场景的优化
空间数据结构:
- BSP树 — 二分空间划分,用于快速可见性查询
- 八叉树 — 层次化空间分割
- BVH — 包围体层次
几何简化:
- 远处物体使用更简单的代理模型
- 细节级别 (LOD) — 自动选择合适的分辨率
6. 阴影
6.1 阴影的产生
定义:阴影发生在物体被挡住时,无法接收来自光源的光。
问题:该点是否能”看到”光源?
6.2 阴影映射 (Shadow Mapping)
基本思想:
- 从光源视点渲染 — 计算从光源到各表面的深度
- 存储阴影贴图 — 记录光源能看到的最近表面距离
- 从相机着色 — 检查该点是否在阴影中
算法步骤:
// 第一遍:从光源渲染
for each object:
for each pixel (xL, yL) in light view:
shadowZ[xL, yL] = min(shadowZ[xL, yL], z(xL, yL))
// 第二遍:从相机渲染
for each object:
for each pixel (x, y):
(xL, yL, zL) = TransformToLightSpace(x, y, z)
in_shadow = (zL > shadowZ[xL, yL] + ε)
image[x,y] = shade(x, y, in_shadow)
参数:
- $\epsilon$ — 偏差(防止自阴影伪影)
优点:
- 支持任意几何体
- 可以预计算
- 效率高
缺点:
- 阴影边界会”锯齿化”(走样)
- 需要额外的纹理内存
- 偏差参数难以调整
改进方法:
- 百分比更接近滤波 (PCF) — 对阴影贴图周围进行平均
- 级联阴影映射 — 使用多个分辨率处理不同距离
- 方差阴影映射 — 使用统计方法
7. 非照相写实渲染
7.1 概述
非照相写实渲染 (Non-Photorealistic Rendering, NPR) 致力于创建看起来像插图、素描或动画的图像,而不是照相现实。
应用:
- 动画和卡通风格游戏(Genshin Impact, Arcane)
- 技术插图
- 医学和生物插图
7.2 卡通着色 (Toon Shading / Cel Shading)
原理:使用离散的颜色级别而不是连续的光照变化,模拟漫画书的风格。
实现:
- 使用查找表或分段函数量化照明值
- 通常 2-3 个颜色级别
例子:
正常Phong着色: 亮度从0.0-1.0平滑变化
卡通着色: 亮度映射到 {0.2, 0.5, 1.0} 三个离散值
7.3 轮廓线提取
轮廓线是NPR中关键的视觉元素,表示物体的边界和特征。
轮廓线类型
- 不连续性 — 表面褶皱、自交点
- 补丁边界 — 表面块之间的边界
- 轮廓线和轮廓 — 在视线上与法线相切的点
- 抛物线 — 高斯曲率为0的点
- 等参线 — 常数参数线
- 曲率线 — 主曲率方向的线
- 阴影 — 阴影区域和光照区域的边界
轮廓提取方法
方法1:法线轮廓线
- 使用着色法线检测视线接近与法线相切的位置
- 优点:快速
- 缺点:线宽难以控制
方法2:程序几何轮廓线
- 渲染一个略微放大的模型以获得轮廓
- 结合Z-缓冲得到轮廓线
- 优点:精确、线宽可控
- 缺点:额外的几何处理
方法3:图像处理边缘检测
- 对渲染的深度/法线图进行边缘检测
- 优点:后处理,不影响渲染流程
- 缺点:可能检测到不期望的边缘
7.4 Gooch着色
原理:用冷暖色转换代替深浅转换,保持可读性同时增加艺术表现力。
颜色映射:
\[I = \frac{1 + \vec{l} \cdot \vec{n}}{2} \cdot k_d + \left(1 - \frac{1 + \vec{l} \cdot \vec{n}}{2}\right) \cdot k_{cool}\]其中:
- 亮处使用温暖色调
- 暗处使用冷色调
- 平滑过渡
特点:
- 保留照明信息
- 增加艺术表现力
- 更易于阅读和辨别
7.5 金属着色
观察:技术插图中金属表面通常用交替的深色和浅色条纹表示,称为各向异性反射。
实现:
- 沿最大曲率的参数轴映射20条条纹
- 条纹强度随机变化(0.0-0.5)
- 最接近光源的条纹设为白色
- 在条纹中心间进行线性插值
效果:模仿车床加工金属表面的外观。
7.6 从技术插图学习
原则 (Tufte的最小有效差异原则):
使所有视觉区分尽可能微妙,但仍然清晰有效。
NPR设计启示:
- 用黑色曲线表示表面边界和轮廓
- 哑光物体:用中等强度着色,根据法线显示冷暖色
- 单一光源提供白色高光
- 元素用简洁有效的方式表达
7.7 应用领域
医学和生物插图:
- 清晰展示解剖结构
- 高度样式化,易于理解
- 突出重点区域
建筑可视化:
- 草图风格表达设计意图
- 保留必要细节,简化视觉信息
总结
着色是从几何和材料参数到最终图像的关键步骤:
- 照明模型 — 从物理直觉出发的简化模型(Phong)
- 着色算法 — 在效率和质量间权衡(平面、Gouraud、Phong)
- 可见性 — Z-缓冲是现代GPU的基础
- 阴影 — 阴影映射是实时渲染的标准技术
- NPR — 扩展渲染范围超越照相写实
- 实时性能 — 现代GPU使复杂着色成为可能
现代渲染通常采用正向和延迟着色管线的混合,以平衡灵活性和性能。
Enjoy Reading This Article?
Here are some more articles you might like to read next: