顶点着色器和片段着色器如何在OpenGL中通信? [英] How vertex and fragment shaders communicate in OpenGL?

查看:393
本文介绍了顶点着色器和片段着色器如何在OpenGL中通信?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我知道

  • 顶点着色器每个顶点运行一次
  • 片段着色器每个片段运行一次

由于片段着色器不适用于每个顶点,但是每个片段都不能将数据发送到片段着色器?顶点数量和片段数量不相等.

如何确定哪个片段属于哪个顶点?

解决方案

要理解这一点,您需要考虑整个渲染管道.顶点着色器的输出(除了特殊输出gl_Position之外)将作为顶点的关联数据"传递到管道的下一级.

虽然顶点着色器一次只处理一个顶点,而根本不关心基元,但是管道的其他阶段确实考虑了基元类型(以及顶点连接性信息).这就是通常所说的原始程序集".现在,我们仍然具有由VS生成的相关数据的单个顶点,但是我们也知道将哪些顶点组合在一起以定义基本的图元,例如点(1个顶点),线(2个顶点)或三角形(3顶点).

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

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

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

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

I really do not understand how fragment shader works.

I know that

  • vertex shader runs once per vertices
  • fragment shader runs once per fragment

Since fragment shader does not work per vertex but per fragment how can it send data to the fragment shader? The amount of vertices and amount of fragments are not equal.

How can it decide which fragment belong to which vertex?

解决方案

To make sense of this, you'll need to consider the whole render pipeline. The outputs of the vertex shader (besides the special output gl_Position) is passed along as "associated data" of the vertex to the next stages in the pipeline.

While the vertex shader works on a single vertex at a time, not caring about primitives at all, further stages of the pipeline do take the primitive type (and the vertex connectivity info) into account. That's what typically called "primitive assembly". Now, we still have the single vertices with the associated data produced by the VS, but we also know which vertices are grouped together to define a basic primitive like a point (1 vertex), a line (2 vertices) or a triangle (3 vertices).

During rasterization, fragments are generated for every pixel location in the output pixel raster which belongs to the primitive. In doing so, the associated data of the vertices defining the primitve can be interpolated across the whole primitve. In a line, this is rather simple: a linear interpolation is done. Let's call the endpoints A and B with each some associated output vector v, so that we have v_A and v_B. Across the line, we get the interpolated value for v as v(x)=(1-x) * v_A + x * v_B at each endpoint, where x is in the range of 0 (at point A) to 1 (at point B). For a triangle, barycentric interpolation between the data of all 3 vertices is used. So while there is no 1:1 mapping between vertices and fragments, the outputs of the VS still define the values of the corrseponding input of the FS, just not in a direct way, but indirectly by the interpolation across the primitive type used.

The formula I have given so far are a bit simplified. Actually, by default, a perspective correction is applied, effectively by modifying the formula in such a way that the distortion effects of the perspective are taken into account. This simply means that the interpolation should act as it is applied linearily in object space (before the distortion by the projection was applied). For example, if you have a perspective projection and some primitive which is not parallel to the image plane, going 1 pixel to the right in screen space does mean moving a variable distance on the real object, depending on the distance of the actual point to the camera plane.

You can disable the perspective correction by using the noperspective qualifier for the in/out variables in GLSL. Then, the linear/barycentric interpolation is used as I described it.

You can also use the flat qualifier, which will disable the interpolation entirely. In that case, the value of just one vertex (the so called "provoking vertex") is used for all fragments of the whole primitive. Integer data can never by automatically interpolated by the GL and has to be qualified as flat when sent to the fragment shader.

这篇关于顶点着色器和片段着色器如何在OpenGL中通信?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆