Attributes

In our programming pipeline, the color of each pixel inside the triangle corresponds to the output of our fragment shader. Since our fragment shader returns (1.0, 0.0, 0.0, 1.0), each pixel is an opaque red (the four values correspond to: red, green, blue, alpha/opacity).

In this example we'll be adding another attribute allowing us to specify a color for each corner of the triangle. To accomplish this we'll first have to extend our Vertex struct and shape as follows:


# #![allow(unused_variables)]
#fn main() {
#[derive(Copy, Clone)]
struct Vertex {
    position: [f32; 2],
    color: [f32; 3],
}
implement_vertex!(Vertex, position, color);

let shape = vec![
    Vertex { position: [-0.5, -0.5], color: [1.0, 0.0, 0.0] },
    Vertex { position: [ 0.0,  0.5], color: [0.0, 1.0, 0.0] },
    Vertex { position: [ 0.5, -0.25], color: [0.0, 0.0, 1.0] }
];
#}

Here we've added a color attribute of type [f32; 3] which corresponds to a vec3 in GLSL, storing the red, green and blue components. In our shape we've specified a solid red, green and blue for each corner. Now we need to update the shaders so that they make us of this new attribute, first the vertex shader:

#version 140

in vec2 position;
in vec3 color;      // our new attribute
out vec3 vertex_color;

uniform mat4 matrix;

void main() {
    vertex_color = color; // we need to set the value of each `out` variable.
    gl_Position = matrix * vec4(position, 0.0, 1.0);
}

The in vec3 color; line should look familiar to you, what is new however is the following line: out vec3 vertex_color;. This line defines a variable that is going to be passed along to the fragment shader, let's update our fragment shader to make use of it and then explore what's going on:

#version 140

in vec3 vertex_color;
out vec4 color;

void main() {
    color = vec4(vertex_color, 1.0);   // We need an alpha value as well
}

Let's see what happens. Our vertex shader is invoked three times, once per vertex. Each vertex returns a different value for vertex_color. OpenGL then determines which pixels are inside the triangle during the rasterization phase, and calls the fragment shader once for each of these pixels. The value of vertex_color that is passed for each pixel is the interpolation of this value depending on the position of the pixel.

For example, pixels that are right next to a vertex will get a value of vertex_color that is equal or very near the value of vertex_color that the vertex shader returned for this vertex. The pixel that is on the middle of the edge between two vertices will get the average of the two values of vertex_color returned by the vertex shader for these two vertices. Pixels that are the middle of the triangle will get the average of the values of all three vertices.

Note: this is because variables by default have the smooth attribute, which is what you want most of the time. It is also possible to specify the flat attribute.

The result should look like this:

The result

You can find the entire source code here.