How does vertex shader pass color information to fragment shader?

RnMss picture RnMss · Aug 15, 2013 · Viewed 19.4k times · Source

In a simple hello-world OpenGL program, which simply draws a static triangle on the window, when I set the 3 vertex of the triangle to red, green and blue color, the triangle is filled with gradient.

But when I use shaders like this:

Vertex Shader:

attribute vec4 aVertex;
attribute vec4 aColor;

varying vec4 vColor;

void main(void) {
    gl_Position = gl_ModelViewMatrix * gl_ProjectionMatrix * aVertex;
    vColor = aColor;
}

where the attributes aVertex and aColor comes from a vertex buffer, passed through a call of glVertexAttribPointer.

Fragment Shader:

varying vec4 vColor;
void main(void) {
    gl_FragColor = vColor;
}

The triangle is still filled with gradient, and here comes the question:

If vertex-shader is calculated per vertex, then each instance of vColor should be assigned with the color of a vertex. And the vertex color should be either red, green, or blue, as set in the vertex buffer.

So where did the gradient come from?

Or, in another word, when did it happen that in the frag-shader, the vColor turns out to be the interpolated color instead of the vertex's?

Answer

jozxyqk picture jozxyqk · Aug 15, 2013

"varying" variables in the fragment shader have the result of linearly interpolating between the values given at the vertex shader stage (based on the relative position of the fragment between the vertices).

That is, when the rasterizer spits out a fragment at a pixel, its position is also given relative to the triangle vertices in barycentric coordinates. These coordinates are then used to interpolate all varying variables from the vertex shader. In most cases, this is what you want and the speed gained from not interpolating is pretty insignificant these days.

Using the keyword "flat" will disable interpolation and instead use the value of the first vertex (I'm not 100% sure "flat" works with varying as I've switched to using in/out keywords with the newer versions of GLSL).

On a side note, I've found this particularly useful if the fragment needs some values from each of the vertices. In this case I use flat out myVertexValue[3] in the geometry shader (for example here).