计算机图形学-笔记(8.2)

351 阅读8分钟

Category:Higher Mathematics & Computer Graphics Application

正文

8 图形管道

8.1栅格化

8.1.2三角栅格化

我们经常要绘制一个二维三角形,在屏幕坐标中具有二维点 p0 = (x0, y0), p1 = (x1, y1) 和 p2 = (x2, y2)。这类似于画线问题,但它有一些自己的微妙之处。与画线一样,我们可能希望根据顶点的值插入颜色或其他属性。如果我们有重心坐标(第 2.7 节),这很简单。例如,如果顶点的颜色为 c0、c1 和 c2,则三角形中重心坐标为 (α, β, γ) 的点的颜色为

image.png

这种类型的颜色插值在其发明者之后在图形中被称为 Gouraud 插值(Gouraud,1971)。

栅格三角形的另一个细微之处在于,我们通常是在共享顶点和边缘的三角形。这意味着我们希望将相邻三角形的隔开化,以便没有孔。我们可以通过使用中点算法绘制每个三角形的轮廓,然后填充内部像素来做到这一点。

这意味着相邻的三角形都沿每个边缘绘制相同的像素。

如果相邻三角形具有不同的颜色,则图像将取决于绘制两个三角形的顺序。避免订单问题并消除漏洞的三角形的最常见方法是使用以下情况,即当且仅当其中心在三角形内,即像素中心的barycentric坐标是在间隔中的( 0,1)。这提出了如果中心正好在三角形的边缘,该怎么办。本节稍后将讨论的方法有几种处理方法。关键观察结果是,barycentric坐标使我们能够决定是否绘制像素以及如果我们从顶点插值颜色,该像素应该是什么颜色。因此,我们对三角形进行栅格化的问题归结为有效地找到像素中心的barycentric坐标(Pineda,1988)。蛮力栅格化算法是:

image.png

该算法的其余部分将外部循环限制为一组较小的候选像素,并使重心计算高效。

我们可以通过找到三个顶点的边界矩形并仅在该矩形上循环以绘制候选像素来增加简单的效率。我们可以使用公式(2.32)计算重心坐标。这产生了算法:

image.png

这里 fij 是由等式 (8.1) 给出的具有适当顶点的线:

image.png

请注意,我们已将测试 α ∈ (0, 1) 与 α > 0 等交换,因为如果所有 α、β、γ 都是正数,那么我们知道它们都小于 1,因为 α β γ = 1。我们也可以只计算三个重心变量中的两个,并从该关系中得到第三个,但不清楚这是否会在算法被增量时节省计算,这在线条绘制算法中是可能的;对 α、β 和 γ 的每一次计算都会对 f(x, y) = Ax By C 的形式进行评估。在内部循环中,只有 x 会改变,并且会改变 1。注意 f(x 1, y) = f(x, y) A。这是增量算法的基础。在外循环中,f(x, y) 的评估变为 f(x, y 1),因此可以实现类似的效率。因为 α、β 和 γ 在循环中以恒定增量变化,所以颜色 c 也是如此。所以这也可以增加。例如,像素 (x 1, y) 的红色值与像素 (x, y) 的红色值相差一个可以预先计算的常数。图 8.5 显示了一个带有颜色插值的三角形示例。

image.png

在三角边缘上处理像素

我们还没有讨论如何处理中心正好在三角形边缘的像素。如果一个像素正好在一个三角形的边上,那么如果有一个相邻的三角形,它也在相邻三角形的边上。没有明显的方法将像素授予一个三角形或另一个。最糟糕的决定是不绘制像素,因为两个三角形之间会产生一个洞。更好但仍然不好的是让两个三角形都绘制像素。如果三角形是透明的,这将导致双色。我们真的很想将像素准确地分配给其中一个三角形,并且我们希望这个过程很简单;只要选择明确,选择哪个三角形并不重要。

image.png

一种方法是注意任何离屏点肯定在共享边缘的一侧,这就是我们要绘制的边缘。对于两个不重叠的三角形,不在边上的顶点在边的相对两侧。这些顶点中的一个恰好与屏幕外点位于边缘的同一侧(图 8.6)。这是测试的基础。数字 p 和 q 是否具有相同符号的测试可以实现为测试 pq > 0,这在大多数环境中非常有效。

请注意,测试并不完美,因为通过边缘的线也可能通过屏幕外点,但我们至少大大减少了有问题的情况。使用哪个屏幕外点是任意的,并且 (x, y) = (−1, −1) 是一个不错的选择。我们需要添加一个检查点是否正好在边上。我们不希望对常见情况进行此检查,即完全内部或外部测试。这表明:

image.png

image.png

我们可能会期望,只有当我们对两个三角形使用完全相同的直线方程时,上述代码才能消除孔洞和重绘。事实上,只有当两个共享顶点在每个三角形的绘制调用中具有相同的顺序时,线方程才是相同的。否则等式可能会翻转符号。这可能是一个问题,具体取决于编译器是否更改了操作顺序。因此,如果需要稳健的实现,可能需要检查编译器和算术单元的细节。上面伪代码中的前四行必须仔细编码,以处理边缘恰好碰到像素中心的情况。

除了适合增量实施之外,还有几个潜在的早期退出点。例如,如果 α 为负,则无需计算 β 或 γ。虽然这很可能会提高速度,但分析始终是一个好主意。额外的分支可能会减少流水线或并发性,并可能减慢代码速度。因此,如果代码是关键部分,请与往常一样测试任何有吸引力的优化。

上述代码的另一个细节是,对于退化三角形,除法可能是除以零,即,如果 fγ = 0。应该正确考虑浮点错误条件,或者需要另一个测试。

8.1.3 剪裁

简单地将基元转换为屏幕空间并对其进行光栅化本身并不能很好地工作。这是因为视图体积之外的图元——尤其是眼睛后面的图元——最终可能会被光栅化,从而导致不正确的结果。例如,考虑图 8.7 中所示的三角形。

image.png

两个顶点在视图体积中,但第三个在眼睛后面。投影变换将此顶点映射到远平面后面的一个无意义的位置,如果允许发生这种情况,三角形将被错误地光栅化。出于这个原因,光栅化之前必须进行剪裁操作,该操作删除可能延伸到眼睛后面的部分图元.

剪裁是图形中的一种常见操作,每当一个几何实体“剪裁”另一个时都需要它。例如,如果您对平面 x = 0 剪裁一个三角形,如果顶点的 x 坐标的符号不完全相同,则该平面会将三角形分成两部分。在大多数裁剪应用中,平面“错误”一侧的三角形部分被丢弃。图 8.8 显示了针对单个平面的此操作。

image.png

在剪裁以准备光栅化时,“错误”侧是视图体积之外的一侧。剪裁视图体积之外的所有几何图形总是安全的——也就是说,剪裁体的所有六个面——但许多系统设法只剪裁近平面。

本节讨论裁剪模块的基本实现。那些对实现工业速度剪辑器感兴趣的人应该看看本章末尾的注释中提到的 Blinn 的书。

实现裁剪的两种最常见的方法是:

  1. 在世界坐标中使用限制截断的观察金字塔的六个平面.

  2. 在齐分之前的4D变换空间。

对每个三角形使用以下方法可以有效地实现这两种可能性(J. Blinn,1996):

image.png

8.1.4 变换前的剪辑(选项 1)

选项 1 有一个简单的实现。唯一的问题是,“六个平面方程是什么?”因为这些方程对于单个图像中渲染的所有三角形都是相同的,所以我们不需要非常有效地计算它们。

出于这个原因,我们只需反转图 5.11 所示的变换,并将其应用于变换后的视图体积的八个顶点:

image.png

平面方程可以从这里推导出来。或者,我们可以使用矢量几何直接从查看参数中获取平面。

8.1.5 在齐次坐标中裁剪(选项 2)

令人惊讶的是,通常实现的选项是在划分之前在齐次坐标中进行裁剪。这里的视图体积是 4D,它以 3D 体积(超平面)为界。这些是

image.png

这些平面非常简单,因此效率比选项 1 更好。它们仍然可以通过将视图体积 [l, r] × [b, t] × [f,n]​​ 转换为 [0, 1]3 来改进.事实证明,三角形的裁剪并不比 3D 复杂多少。