# 第十一章 深度

## 11.1 可见性（visibility）

在物理世界中，如果物体 A 位于物体 B 的前方，则来自物体 B 方向的光，会在到达摄影机前被物体 A 挡住，因而不会出现在画面中（例如，图10.2中的蓝色六边形）。在计算机图形学中，我们需要以计算的方式对其建模。

对于确保只有对摄影机可见的面（surface）出现在渲染画面（image）中，有很多实现方式。其中一种是，将三角面根据它们在摄影机方向的深度进行排序，再由后至前绘制它们。这一思想在于最前面的三角面会重新绘制并覆盖在其后方的三角面上，从而得到正确的渲染画面。这一方法称作画家方式（painter's approach）的思路，它存在一些难以处理的点。例如，一个场景中也许包含有相互穿插的三角面。它也许还会包含图 11.1 中可见性循环（visibility cycles）这样的穿插三角面的情况。

![三个三角面构成了一个可见性循环。不可能将它们由后至前排序。](https://3079340014-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MR9b16cwt_hvqTAbEF_%2Fuploads%2FKRhSpP4cS7vIRVnT1vB5%2FFigure_11-1.jpg?alt=media\&token=710c4698-edca-40ba-b7f1-bf541ac47f49)

另一种广泛使用的方式，我们将会在第 20 章中讨论，即光线追踪（ray casting）。这个方式，是对每一个像素，显式的计算沿着该像素方向的视线与场景的交点。使用距离最近的交点来计算该像素的颜色。

可见性计算也可以用于加速渲染过程。如果我们知道一个物体会被挡住，从一开始我们就不用去渲染它。这可以使用在诸如室内场景中，我们通常不会看到房间外很远的东西。在这种情境下，我们可以使用保守可见性检测（conservative visibility test）；这一检测可以很快告诉我们一个物体是可能，或者完全不可能具有可见性。如果一个物体可能是可见的，那么我们使用 z-buffer 继续渲染该物体。但如果该物体完全不可能可见，那么我们可以将整个绘制省略掉。

## 11.2 基础数学模型

在 OpenGL 中，我们使用 z-buffer 来计算可见性。为了使用 z-buffer，除了一个点 $$\[x\_n, y\_n]$$坐标系向量之外，我们还需要一个深度值。为了实现这一点，对于视角坐标系下描述的每一个点，我们使用以下矩阵表达式来定义坐标系$$\[x\_n, y\_n, z\_n]^t$$：

$$
\left\[ \begin{matrix} 	x\_nw\_n \ y\_nw\_n \ z\_nw\_n \ w\_n 	\end{matrix} \right]
===================================================================================

# \left\[ \begin{matrix} 	x\_c \ y\_c \ z\_c \ w\_c 	\end{matrix} \right]

\left\[ \begin{matrix}
s\_x & 0 & -c\_x & 0 \\
0 & s\_y & -c\_y & 0 \\
0 & 0 & 0 & 1 \\
0 & 0 & -1 & 0
\end{matrix} \right]
\left\[ \begin{matrix} 	x\_e \ y\_e \ z\_e \ 1 	\end{matrix} \right]
.
\tag{11.1}
$$

同样，其原始输出称为裁剪坐标（clip coordinates），并且，同之前一样，为了得到$$x\_n$$​与$$y\_n$$​的值，我们需要除以$$w\_n$$​的值。但是现在我们还有$$z\_n=\frac{-1}{z\_e}$$的值。我们计划使用$$z\_n$$​的值在 z-buffer 中进行深度比较。

首先，我们先来验证公式（11.1）给出的$$z\_n$$​值的确可以用于深度比较。给定两点$$\tilde{p}^1$$​和$${\tilde{p}}^2$$在视角坐标系下的坐标$$\[{x\_e}^1, {y\_e}^1, {z\_e}^1, 1]^t$$和$$\[{x\_e}^2, {y\_e}^2, {z\_e}^2, 1]^t$$，假设它们均位于视点前方（即$${z\_e}^2 < {z\_e}^1$$）。那么 $$- \frac{1}{{z\_e}^2} < - \frac{1}{{z\_e}^1}$$，意味着$${z\_n}^2 < {z\_n}^1$$。

汇总在一起，我们现在可以将视角坐标系向归一化设备坐标系（NDC）转换的过程考虑为纯粹的 3D 几何变换。这一变换通常既非线性，也非仿射，而被称作三维投影变换（3D projective transformation）。

投影变换很有意思；例如，在上述的情形中，任何$$z\_e=0$$​的点都会导致除零的问题。就目前而言，对我们来说关于投影变换最重要的方面在于（随后会展开解释），它可以保留点与点之间的共线性和共面性（如图 11.2 和 11.3）。共线性意味着，如果三个或多个点位于一条直线上，变换后的点仍然会位于一条线上。

共面性的意义在于，我们知道对于在固定某个三角面上的固定点$$a, b$$ 以及 $$c$$ ，我们会有$$z\_n = ax\_n + by\_n + c$$​。因此，在已知三角面上三个顶点时，通过 2D 平面图像上的线性插值，可以正确计算得到其中某点的$$z\_n$$值。（附录 B 中有关于线性插值的内容）。

注意，尽管如此，投影变换不会保留距离。再次看看图 11.2 以及 11.3，我们可以看出，在二维平面上距离相等的像素，并不对应着视角空间下距离相等的几何点。同样，这些等距分布的像素，也不会对应归一化设备坐标系下等距的点。

由于距离上的失真，在屏幕空间下对$$z\_e$$​的线性插值会得到错误的结果。考虑图 11.4 所示的情况，有两个交叉的线段，分别为橘黄色与蓝色。其顶点的$$z\_e$$​值如图所示。两线段恰好在图像的正中间处交叉，因此在正确的图像中，上半部分应当为蓝色，而下半部分应当为橘黄色。如果我们对每个线段在图像空间中进行线性插值，
