顶点和片段着色器如何在 OpenGL 中通信?

How vertex and fragment shaders communicate in OpenGL?

我真的不明白片段着色器是如何工作的。

我知道

由于片段着色器不是按顶点工作而是按片段工作如何将数据发送到片段着色器?顶点数量和片段数量不相等。

它如何判断哪个片段属于哪个顶点?

答案是他们不会——至少不会直接这样做。还有一个叫做 "the rasterizer" 的东西位于管道中的顶点处理器和片段处理器之间。光栅化器负责收集从顶点着色器出来的顶点,将它们重新组装成图元(通常是三角形),将这些三角形分解成 "rasters" 个(部分)覆盖像素,并将这些片段发送到片段着色器.

这是一个(大部分)固定功能的硬件,您不能直接对其进行编程。您可以进行一些配置调整,这些调整会影响将什么视为基元以及将其作为片段产生什么,但在大多数情况下,它只是在顶点着色器和片段着色器之间执行其操作。

要理解这一点,您需要考虑整个渲染管道。顶点着色器的输出(除了特殊输出 gl_Position 之外)作为顶点的 "associated data" 传递到管道中的下一个阶段。

虽然顶点着色器一次只处理一个顶点,根本不关心图元,但管道的后续阶段确实会考虑图元类型(和顶点连接信息)。这就是通常所说的 "primitive assembly"。现在,我们仍然拥有 VS 生成的具有关联数据的单个顶点,但我们也知道哪些顶点被组合在一起以定义基本图元,如点(1 个顶点)、线(2 个顶点)或三角形(3顶点)。

在光栅化过程中,为输出像素光栅中属于图元的每个像素位置生成片段。这样做时,定义基元的顶点的关联数据可以在整个基元上进行 插值 。在一行中,这很简单:完成线性插值。让我们用一些关联的输出向量 v 来调用端点 A 和 B,这样我们就有 v_A 和 v_B。通过这条线,我们在每个端点得到 v 的内插值 v(x)=(1-x) * v_A + x * v_B,其中 x 在 0 的范围内(在A 点)到 1(B 点)。对于三角形,使用所有 3 个顶点的数据之间的重心插值。因此,虽然顶点和片段之间没有 1:1 映射,但 VS 的输出仍然定义了 FS 的相应输入的值,只是不是以直接方式,而是通过使用的原始类型的插值间接.

到目前为止我给出的公式有点简化。实际上,默认情况下会应用透视校正,通过以考虑透视失真影响的方式修改公式来有效地应用透视校正。这只是意味着插值应该像在对象 space 中线性应用一样(在应用投影失真之前)。例如,如果你有一个透视投影和一些不平行于图像平面的基元,在屏幕 space 中向右移动 1 个像素确实意味着在真实物体上移动可变距离,具体取决于距离相机平面的实际点。

您可以通过对 GLSL 中的 in/out 变量使用 noperspective 限定符来禁用透视校正。然后,按照我的描述使用 linear/barycentric 插值。

您还可以使用 flat 限定符,这将 完全禁用 插值。在那种情况下,只有一个顶点的值(所谓的 "provoking vertex")用于整个图元的所有片段。整数数据永远不能由 GL 自动插值,并且在发送到片段着色器时必须符合 flat