屏幕中每像素剪裁三角形 space

Clipping triangles in screen space per pixel

据我了解,在 OpenGL 中,多边形通常在 clip space 中被裁剪,并且只有那些三角形(或三角形的一部分,如果裁剪过程将它们分开)在与 +- w 的比较中幸存下来。然后,这需要实施多边形裁剪算法,例如 Sutherland-Hodgman。

我正在实现我自己的 CPU 光栅器,现在我想避免这样做。我有可用的顶点 NDC 坐标(不是真正的 规范化 ,因为我没有剪裁任何东西,所以位置可能不在 [-1, 1] 范围内)。我想为所有像素插入这些值,并且只绘制 NDC 坐标在 x、y 和 z 维度中落在 [-1, 1] 范围内的像素。然后我会另外执行深度测试。

这行得通吗?如果是,插值会是什么样子?我可以使用 OpenGl spec (page 427 14.9) formula for attribute interpolation as described here 吗?或者,我是否应该使用用于所有 3 个坐标的深度 (z) 插值的公式 14.10(我真的不明白为什么在那里使用不同的)?

更新: 我尝试通过两种方法对每个像素的 NDC 值进行插值:

w0, w1, w2 是顶点的重心权重。

1) float x_ndc = w0 * v0_NDC.x + w1 * v1_NDC.x + w2 * v2_NDC.x; float y_ndc = w0 * v0_NDC.y + w1 * v1_NDC.y + w2 * v2_NDC.y; float z_ndc = w0 * v0_NDC.z + w1 * v1_NDC.z + w2 * v2_NDC.z;

2) float x_ndc = (w0*v0_NDC.x/v0_NDC.w + w1*v1_NDC.x/v1_NDC.w + w2*v2_NDC.x/v2_NDC.w) / (w0/v0_NDC.w + w1/v1_NDC.w + w2/v2_NDC.w); float y_ndc = (w0*v0_NDC.y/v0_NDC.w + w1*v1_NDC.y/v1_NDC.w + w2*v2_NDC.y/v2_NDC.w) / (w0/v0_NDC.w + w1/w1_NDC.w + w2/v2_NDC.w); float z_ndc = w0 * v0_NDC.z + w1 * v1_NDC.z + w2 * v2_NDC.z;

裁剪+深度测试总是这样:

if (-1.0f < z_ndc && z_ndc < 1.0f && z_ndc < currentDepth && 1.0f < y_ndc && y_ndc < 1.0f && -1.0f < x_ndc && x_ndc < 1.0f)

情况 1) 对应于使用等式 14.10 进行插值。情况 2) 对应于使用等式 14.9 进行插值。

结果documented in gifs on imgur. 1) 当第二个立方体在相机后面或当我进入立方体时会发生奇怪的事情。 2) 奇怪的伪影不可见,但随着相机接近顶点,它们开始消失。因为这是 perspective correct interpolation of attributes 顶点(更靠近相机?)具有更大的权重,所以一旦顶点被剪裁,该信息就会以强权重插值到三角形像素。

这一切是意料之中还是我做错了什么?

我看不出有任何原因这行不通。但它会比传统剪辑慢很多。请注意,靠近投影中心的三角形可能会遇到麻烦,因为它们会非常小并且可能会导致重心坐标计算出现问题。

等式 14.9 和 14.10 之间的区别在于,深度基本上是 z/w(并重新映射到 [0, 1])。由于透视划分已经发生,因此必须在插值期间将其保留。

剪裁近平面并不是绝对必要的,除非三角形在相机中达到或超过 0-space Z。一旦发生这种情况,齐次坐标数学就会变得奇怪。

大多数硬件只会在三角形延伸超过剪辑外的屏幕宽度时才费心剪辑 space 或者如果它们穿过零的相机 Z。这种裁剪称为 "guard-band clipping",它节省了大量性能,因为裁剪并不便宜。

所以是的,数学可以很好地工作。在设置扫描线时,您要做的主要事情是找出每条扫描线 start/end 在屏幕上的位置。两种方式的插值数学都是相同的。