第 5 讲:可见性、深度缓冲与局部光照
这一讲其实在回答两个不同问题:
- 一个片元能不能被相机看见?
- 如果能看见,它应该有多亮、是什么颜色?
前者是可见性,后者是着色。把二者混在一起,就会误以为“背光”与“被挡住”是一回事。
1. 背面剔除只解决朝向
封闭物体的很多面背向相机,通常不可能被直接看到。给三角形顶点 ,法线可取
若顶点逆时针为正面,则投影后的绕序或法线与视线的点积可用于判断正反面。背面剔除能减少后续工作量,但有两个边界:
- 它不判断两个正面之间谁挡住谁。
- 对双面薄片、开口网格或绕序不一致的模型,盲目开启会丢掉本应显示的面。
2. 全局排序为什么难
Painter 算法模仿油画:从远到近画,后画的覆盖先画的。它对简单场景有效,但相交多边形和循环遮挡会让“谁在谁后面”没有全局一致顺序。
BSP 树用一个多边形所在平面递归划分空间。给定视点后,可按背面子树、当前平面、正面子树的顺序遍历,得到远到近的绘制序列。代价是建树和切分几何较复杂,场景频繁变化时不方便。
3. Z-buffer:每个像素自己决定
Z-buffer 不尝试给所有三角形排序,而是给每个采样位置保存目前最近的深度:
- 深度缓冲初始化为最远值。
- 光栅化一个三角形,求出每个覆盖样本的深度。
- 新深度更近时,更新颜色和深度;否则丢弃。
这样绘制顺序不再重要,时间复杂度近似与产生的片元数成正比,空间开销是一张与颜色缓冲同分辨率的深度图。
深度精度不是均匀分布的。透视投影后,精度更多集中在近裁剪面附近;near 设得过小、far 设得过大,容易产生 Z-fighting。
4. 重心坐标把顶点值送进三角形内部
三角形内任一点都可表示为
三角形内部有 。权重可通过子三角形面积比或边函数求出。于是深度可插值为
同样的方法还能插值颜色、法线和纹理坐标。若每像素有多个子样本,就为每个样本分别保存深度与颜色,最后再合并,才能正确处理边缘处的局部覆盖。
5. Blinn–Phong 模型
课件使用局部反射模型,把一点的亮度拆成环境、漫反射与镜面反射:
所有方向都应先归一化:
- :表面法线。
- :从着色点指向光源。
- :从着色点指向相机。
- :半程向量。
漫反射遵循 Lambert 余弦律,与观察方向无关;镜面项在法线靠近半程方向时最强,指数 越大,高光越窄。环境项是对间接光的粗略常量近似。
点光源还常带距离衰减,例如
多个光源的局部贡献可相加。这个模型计算便宜、直观,但不描述全局多次反射,也不保证物理能量守恒。
6. 着色频率决定“在哪算”
同一个反射模型可以放在不同阶段:
- Flat shading:一个三角形共用一个法线和颜色,适合有明确棱角的表面。
- Gouraud shading:在顶点算光照,再插值颜色;速度快,但小而尖的高光可能落不到顶点上。
- Phong shading:插值顶点法线并重新归一化,再逐片元算光照;高光更稳定。
Phong shading 与 Phong reflection model 不是同一个概念:前者说计算频率,后者说反射公式。
顶点法线通常把邻接面的法线加权平均后归一化。若希望保留硬边,就不能跨棱边平均同一份法线,而要为同一空间位置存多组顶点属性。
7. 三个层次不要混
- 背面剔除:这个面是否朝向相机。
- 深度测试:这个样本是否被更近表面挡住。
- 光照着色:可见表面如何与光源、材质和相机相互作用。
按这个顺序分析,很多“为什么全黑”“为什么穿帮”“为什么高光不见了”的问题会自然落到正确环节。