【发布时间】:2015-04-04 15:53:25
【问题描述】:
我真的不明白片段着色器是如何工作的。
我知道
- 顶点着色器每个顶点运行一次
- 片段着色器每个片段运行一次
由于片段着色器不是按顶点工作,而是按片段工作,它如何将数据发送到片段着色器?顶点数量和碎片数量不相等。
它如何决定哪个片段属于哪个顶点?
【问题讨论】:
标签: opengl graphics glsl fragment-shader vertex-shader
我真的不明白片段着色器是如何工作的。
我知道
由于片段着色器不是按顶点工作,而是按片段工作,它如何将数据发送到片段着色器?顶点数量和碎片数量不相等。
它如何决定哪个片段属于哪个顶点?
【问题讨论】:
标签: opengl graphics glsl fragment-shader vertex-shader
要理解这一点,您需要考虑整个渲染管道。顶点着色器的输出(除了特殊输出gl_Position)作为顶点的“关联数据”传递到管道中的下一个阶段。
虽然顶点着色器一次只在一个顶点上工作,根本不关心基元,但管道的进一步阶段确实会考虑基元类型(和顶点连接信息)。这就是通常所说的“原始装配”。现在,我们仍然拥有由 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(在点乙)。对于三角形,使用所有 3 个顶点的数据之间的重心插值。因此,虽然顶点和片段之间没有 1:1 映射,但 VS 的输出仍然定义了 FS 的相应输入的值,只是不是以直接方式,而是通过使用的原始类型的插值间接地定义。
到目前为止我给出的公式有点简化。实际上,默认情况下,应用透视校正,通过修改公式以考虑透视的失真影响。这仅仅意味着插值应该像在对象空间中线性应用一样起作用(在应用投影失真之前)。例如,如果您有透视投影和一些不平行于图像平面的图元,则在屏幕空间中向右移动 1 个像素确实意味着在真实对象上移动可变距离,具体取决于实际点到的距离相机平面。
您可以通过对 GLSL 中的 in/out 变量使用 noperspective 限定符来禁用透视校正。然后,按照我的描述使用线性/重心插值。
您还可以使用flat 限定符,它将完全禁用插值。在这种情况下,只有一个顶点(所谓的“激发顶点”)的值用于整个图元的所有片段。整数数据永远不能由 GL 自动插值,并且在发送到片段着色器时必须限定为 flat。
【讨论】:
答案是他们没有——至少不是直接的。还有一个叫做“光栅化器”的东西,它位于管道中的顶点处理器和片段处理器之间。光栅化器负责收集来自顶点着色器的顶点,将它们重新组装成图元(通常是三角形),将这些三角形分解为(部分)覆盖像素的“光栅”,并将这些片段发送到片段着色器。
这是(大部分)固定功能的硬件,您不能直接编程。您可以进行一些配置调整,这些调整会影响它作为基元处理的内容以及作为片段生成的内容,但在大多数情况下,它只是在顶点着色器和片段着色器之间进行操作。
【讨论】: