Change triangle color in OpenGL 4.5

Go to OpenGL Home

Last time we created a triangle in a modern OpenGL way using shaders, a VBO and a VAO. However the color of the triangle is still hardcoded in the fragment shader and we’d like to pass color in the same way we done with position.

Triangle color

Triangle 3 different vertex colors

In this tutorial we will going to look at:

  • Modifying VertexFormat structure adding a new attribute (color)
  • Modify our CreateTriangleModel method from GameModels class
  • Modify vertex shader and pixel shader

1. Modifying VertexFormat

First thing we have to modify is the VertexFormat structure. Now, beside its position, each vertex will have its color too.

struct VertexFormat

  glm::vec3 position;
  glm::vec4 color;

 VertexFormat(const glm::vec3 &iPos, const glm::vec4 &iColor)
    position = iPos;
    color = iColor;


There are 4 color channels RGBA with values between [0.0 – 1.0f]. A is the alpha channel, for the moment we set it to 1.0 (full intensity). The easiest way to represent color in our code is to use a glm::vec4. However you can use four separate floats or create your own structure.  To not waste time creating my own type I preferred to use a glm::vec4 type

2.CreateTriangleModel modification

Next we have to add color values to our vertices in CreateTriangleModel method in GameModels class and also set color attribute on qualifier (pipe) 1. We will have positions on pipe 0 and colors on pipe 1. However the order we set the attributes doesn’t matter as long as they match layouts from vertex shader.

void GameModels::CreateTriangleModel(const std::string& gameModelName)
 unsigned int vao;
 unsigned int vbo;

 glGenVertexArrays(1, &vao);

 std::vector<VertexFormat> vertices;
 vertices.push_back(VertexFormat(glm::vec3( 0.25, -0.25, 0.0),
                                 glm::vec4( 1, 0, 0, 1))); //red
 vertices.push_back(VertexFormat(glm::vec3(-0.25, -0.25, 0.0),
                                 glm::vec4( 0, 1, 0, 1))); //green
 vertices.push_back(VertexFormat(glm::vec3( 0.25, 0.25, 0.0),
                                 glm::vec4( 0, 0, 1, 1))); //blue

 glGenBuffers(1, &vbo);
 glBindBuffer(GL_ARRAY_BUFFER, vbo);
 glBufferData(GL_ARRAY_BUFFER, sizeof(VertexFormat) * 3, &vertices[0], GL_STATIC_DRAW);
 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFormat), (void*)0);
 glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(VertexFormat), (void*)12);

 Model myModel;
 myModel.vao = vao;
 GameModelList[gameModelName] = myModel;


For first vertex we set color red (1, 0, 0, 1), for second vertex we set the color green(0,1,0,1) and for the third vertex we set the color to blue(0,0,1,1). Finally we call glEnableVertexAttribArray() and glVertexAttribPointer() methods again for the color attribute. This way OpenGL knows about our attributes and vertex format.

Remember that we have an interleaved buffer. On pipe 0 we set position attribute which we talked about in our previous tutorial. And now on pipe 1 we send 4 floats not normalized, knowing that next vertex appears every sizeof(VertexFormat) and the offset for the first color element (Red from RGBA) is 12 because position attributes have 3 floats and one float has 4 bytes. For example if position had just 2 floats (x and y without z) then the offset for colors would have been 8. If position would have had 4 floats then the offset for colors would have been 16.

3.Modifying vertex and fragment shader

It’s time to modify our vertex shader again. Based on my simplified GPU schema below we can clearly see that we have to add another attribute.

simplified GPU 2

Simplified GPU again. Notice that this schema is abstract and is meant to show just a flow. A real GPU is far more complicated and has more components than this. I didn’t put any cache memory, cores, thread scheduler, peripherials etc. If you are interested in a GPU architecture you can begin reading this :

The code for the vertex shader is:

//vertex shader
#version 450 core //lower this version if your card does not support GLSL 4.5
layout(location = 0) in vec3 in_position;
layout(location = 1) in vec4 in_color;

out vec4 color;

void main()
  color = in_color;
  gl_Position = vec4(in_position, 1);

Now, we have the position and color attributes set in vertex shader. However it’s not vertex shader’s job to process colors, so we must pass this attribute to the fragment shader. Passing variables between stages is done using in/out variables. To send them correctly, the name and the type of the variable must be the same in both shaders. In our case, we have out vec4 color in vertex shader and in vec4 color in fragment shader

//fragment shader
#version 450 core

layout(location = 0) out vec4 out_color;

in vec4 color;

void main()
  out_color = color;

As you can see the fragment shader changed a litte bit. I changed the name of the variables. Now out_color is the main output color instead of color. And color became the in variable from vertex shader. Also notice that we explicitly specfiied a location (0) for the out_color. The last stage of the graphical pipeline is represented by the framebuffer also called back buffer or main buffer. This buffer is used to display the final image on the screen. In a double buffered rendering program, the color location is set on index 0. In complex scenes we can output multiple variables from fragment shader and also render to a customized buffer. At this stage we can also do pixel operations like scissor test, stencil test or depth test. However we will talk about this techniques in some later tutorials because at the moment they are a bit advanced.

Vertex Shader Fragment Shader Framebuffer

Vertex Shader ->Fragment Shader-> Framebuffer

You may wonder why your triangle has other colors beside: red, green and blue (colors used when we created the triangle). We’ll answer this in the next article when we talk more about the pipeline.


blog comments powered by Disqus