[简化版 GAMES 101] 计算机图形学 12:可见性与 Z‑Buffer 深度缓存

简化版 GAMES 101 计算机图形学 12:可见性与 Z‑Buffer 深度缓存

  • [Bilibili 同步视频](#Bilibili 同步视频)
  • [一、光栅化:把三角形 "铺" 在像素上](#一、光栅化:把三角形 “铺” 在像素上)
  • 二、可见性难题:物体互相遮挡,怎么画才对?
    • [1. 朴素思路:画家算法(Painter's Algorithm)](#1. 朴素思路:画家算法(Painter’s Algorithm))
  • [三、真正的工业级方案:深度缓存 Z‑Buffer 🚀](#三、真正的工业级方案:深度缓存 Z‑Buffer 🚀)
  • [四、Z‑Buffer 为什么强?3 个关键优势](#四、Z‑Buffer 为什么强?3 个关键优势)
    • [1. 时间复杂度极低](#1. 时间复杂度极低)
    • [2. 绘制顺序无关](#2. 绘制顺序无关)
    • [3. 完美解决循环遮挡](#3. 完美解决循环遮挡)
  • 五、细节与局限:这些坑要知道
    • [1. 深度图的颜色理解](#1. 深度图的颜色理解)
    • [2. 浮点精度问题](#2. 浮点精度问题)
    • [3. Z‑Buffer 处理不了透明物体](#3. Z‑Buffer 处理不了透明物体)
    • [4. MSAA 反走样 + 深度缓存](#4. MSAA 反走样 + 深度缓存)
  • [六、总结 ✨](#六、总结 ✨)

Bilibili 同步视频

简化版 GAMES 101 计算机图形学 12:可见性与 Z‑Buffer 深度缓存

在计算机图形学的世界里,把三维空间里的物体 "画" 到二维屏幕上,是一切渲染的起点✨。我们每天看到的游戏画面、3D 模型、特效动画,底层都离不开光栅化可见性判断这两大核心。今天就从最基础的三角形绘制开始,一步步讲清:屏幕为什么会锯齿、怎么正确处理遮挡、Z‑Buffer 到底如何工作。


一、光栅化:把三角形 "铺" 在像素上

屏幕本质就是密密麻麻的像素小方块 📱,每个像素颜色统一、位置固定。想把一个三角形显示出来,最直接的方式就是:

像素中心对三角形覆盖的区域做信号采样。

但这里会出现一个经典问题 ------走样(Aliasing)

直接采样会让边缘出现锯齿、闪烁,观感非常粗糙。

那怎么解决?

答案很优雅:先模糊,再采样

先对信号做低通滤波,把高频信息抹平,再去采样,混叠自然消失,画面立刻顺滑很多。

从频域角度看,这个顺序绝对不能颠倒

  • 先采样 → 频谱被搬移 → 产生不可逆转的混叠

  • 再模糊 → 只能截断信号,混叠依然存在

这就是为什么 "先采样后模糊" 永远行不通。


二、可见性难题:物体互相遮挡,怎么画才对?

单个三角形很好画,但真实场景里有成千上万个三角形,距离相机远近不同、互相穿插遮挡。

核心问题只有一个:

谁在前、谁在后?怎么让近处挡住远处?

1. 朴素思路:画家算法(Painter's Algorithm)

最直观的方案,来自油画大师的创作逻辑:

从远到近依次绘制

先画远山 → 再画草地 → 最后画树木,新内容覆盖旧内容,遮挡关系自然正确。

但它有两个致命短板:

  1. 排序昂贵

    n 个三角形需要 O (n log n) 复杂度排序,大规模场景扛不住。

  2. 深度环无法解决

    三个三角形两两互相遮挡:P 覆盖 Q、Q 覆盖 R、R 又覆盖 P。

    这种循环深度关系,根本排不出先后,画家算法直接失效。

现实渲染里,它只能用在极简单的场景。


三、真正的工业级方案:深度缓存 Z‑Buffer 🚀

为了搞定复杂遮挡,图形学引入了深度缓存(Depth Buffer / Z‑Buffer)

它的思想极其聪明:放弃对物体排序,只对像素负责

核心思想

每个像素,只记录一件事:

当前能看到的 "最近深度"(离相机最近的距离)。

渲染时同步维护两张图:

  • Frame Buffer:最终显示的彩色画面

  • Depth Buffer:只存每个像素的深度信息

深度约定:

  • 深度值 越小 → 离相机越近

  • 深度值 越大 → 离相机越远

Z‑Buffer 执行流程(超清晰)

  1. 初始化 Depth Buffer → 所有像素深度 = 无限远

  2. 遍历任意顺序的三角形,光栅化到对应像素

  3. 对每个覆盖像素:

    • 计算当前三角形在该像素的深度值

    • 如果 < 缓存里的深度:更新深度 + 绘制颜色

    • 如果 ≥ 缓存里的深度:直接丢弃,被遮挡

它完全不关心三角形先后顺序,只在像素级别做 "最小值更新"。

举个栗子一眼看懂

  • 先画红色三角形,某像素深度 = 5

  • 再画蓝色三角形,同一像素深度 = 3

  • 3 < 5 → 蓝色覆盖红色,深度更新为 3

如果深度 = 8:

8 > 5 → 直接忽略,不画也不更新。


四、Z‑Buffer 为什么强?3 个关键优势

1. 时间复杂度极低

假设每个三角形覆盖常数个像素,整体复杂度为 O(n)

不是排序,只是逐像素求最小值,效率碾压画家算法。

2. 绘制顺序无关

不管先画近还是先画远,只要深度判断正确,结果完全一致。

工程上极其友好,不用操心绘制顺序。

3. 完美解决循环遮挡

三角形互相穿插?无所谓。

Z‑Buffer 逐像素判断,谁近显示谁,彻底告别深度环死局。


五、细节与局限:这些坑要知道

1. 深度图的颜色理解

深度值越小(近)→ 颜色偏暗

深度值越大(远)→ 颜色偏亮

所以近处物体在深度图里通常偏黑,远处偏白。

2. 浮点精度问题

实际用浮点数存储深度,几乎不会出现两个深度完全相等

极端巧合下的深度冲突,工程上有专门处理方案。

3. Z‑Buffer 处理不了透明物体

透明物体需要混合颜色,不能简单 "近覆盖远"。

透明渲染必须用额外算法,Z‑Buffer 无能为力。

4. MSAA 反走样 + 深度缓存

想让边缘更顺滑?用 MSAA 多采样。

这时要对每个采样点单独存深度 ,而不是一个像素只存一个深度。

这也是很多作业与实战渲染的高分提升点。


六、总结 ✨

  • 光栅化:像素中心采样 → 先模糊后采样才是正确反走样。

  • 画家算法:简单直观,但无法处理复杂遮挡与深度环。

  • Z‑Buffer:逐像素记录最近深度,任意顺序、O (n) 效率、工业标准。

  • 局限:不支持透明,深度需注意浮点精度。

到这里,光栅化与可见性的核心逻辑就全部讲完了。

下一篇,我们进入更美的世界:光照与着色(Shading),看 3D 物体如何拥有真实质感。

相关推荐
牧艺4 天前
用 Next.js + React Three Fiber 打造 3D 快递仓储可视化
前端·three.js
Yuhua_Cesium_Threejs6 天前
《在 Cesium 中用 Three.js 实现气象雷达三维体渲染——从原理到性能优化》
three.js
牧艺6 天前
用 Three.js 实现一个浏览器端 3D 看车的项目
前端·three.js
风子1111 天前
pong_Day 3:AI 对手球拍 + 计分系统 + 胜负判定
godot
_Emma_11 天前
【DRM&Graphic】Linux图形与显示框架
linux·驱动开发·图形渲染·显示器
凌涘11 天前
从零掌握 CSS 3D:用几行代码让网页"立"起来
three.js
叶帆11 天前
【YFIOs】用C#开发硬件之设备上云
开发语言·unity·c#
久数君11 天前
AI三维建模工具“造形家”:地理场景三维化的高效解决方案
unity·glb·ai算法·ai三维建模工具·地图框选·造形家·城市建筑模型
柳杉11 天前
我用Threejs 搓了一个 3D 中国地图设计器,开箱即用
前端·three.js·数据可视化
会思考的猴子12 天前
Unity VFX 属性 Postion 和 TargetPostion
unity