So this is just a quick little update of what we can do with our triangle from the previous tutorial, while the next tutorial is worked on. The goal of this tutorial is to pass a second attribute of color to our Vertex Shader so that we will interpolate the different colors which each vertex is set to be. The source code can be found here.
First we will update our vertex and fragment shaders. Our new vertex shader looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//Vertex Shader const GLchar* VertexShaderSource[] = { "#version 430\n \ in vec2 VertexPosition;\ in vec3 VertexColor;\ out vec3 VFragColor;\ void main() \ {\ VFragColor= VertexColor;\ gl_Position= vec4(VertexPosition, 0, 1); \ }" }; |
So what is happening with our vertex shader here? The first thing we do is declare the version we are working with. Since we are working with OpenGL version 4.3 in these first two tutorials, we declare #version 430. On the next two lines we declare our input attributes with the in keyword to the vertex shader. The first VertexPosition we dealt with in Tutorial 1. The new input attribute is VertexColor. This attribute will be taken as input of three float values to specify red, green, and blue values for each vertex. This color value is set equal to VFragColor. VFragColor itself is an output value, as specified by the GLSL keyword out. This keyword tells the vertex shader (or other shader stages) to pass along this variable to the next part in the shader program we are using. So in this case we are passing this color value to our fragment shader.
Next we update our fragment shader.
1 2 3 4 5 6 7 8 9 10 11 |
//Fragment Shader const GLchar* FragmentShaderSource[] = { "#version 430\n \ in vec3 VFragColor;\ out vec4 FragColor; \ void main() \ { \ FragColor = vec4(VFragColor, 1.0); \ } " }; |
With our fragment shader, we now take as input the VFragColor attribute that was output from our vertex shader (Note: the name must be the same as the output from the vertex shader). We set FragColor equal to VFragColor’s red, green, and blue values along with an alpha value of 1. Colors specified for each vertex of a triangle will be interpolated between the vertices.
What we need to do now is update our vertices array in the InitGL() function, so we can also pass a color for each vertex. At the end of each vertex we will be adding a color. To the first vertex(0., .5), red (1.0, 0.0, 0.0) is added. The second vertex (-.5, -.5) has a color of green (0.0, 1.0, 0.0) added. And to the third (.5, -.5), blue (0.0, 0.0, 1.0) has been added. Along with that I have changed the background color to black so that we can better see each vertex on the triangle, since one will be colored blue.
1 2 3 4 5 6 7 8 9 10 11 |
//initialize clear color to blue glClearColor(0.f, 0.f, 0.f, 0.f); //vertex attribute data //now with more color! GLfloat vertices[] = { 0.f, .5f, 1.0f, 0.0f, 0.0f, -.5f, -.5f, 0.0f, 1.0f, 0.0f, .5f, -.5f, 0.0f, 0.0f, 1.0f }; |
Following that I changed a little bit of code when binding our buffer data. To avoid having to deal with the intricacies of the size of the data we are passing to glBufferData, I have changed:
1 |
glBufferData(GL_ARRAY_BUFFER, 2 * 3 * sizeof(GLfloat), vertices, GL_STATIC_DRAW); |
to:
1 |
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); |
Just two more things need to be done. First we need to find the location of the VertexColor attribute so we know where to pass the color values for each vertex. This is done just like VertexPosition in the previous tutorial, with the glGetAttribLocation function:
1 2 3 4 5 6 |
VertexColor = glGetAttribLocation(ProgramID, "VertexColor"); if (VertexColor == -1) { std::cout << "Unable to locate VertexColor in glsl program" << std::endl; return false; } |
Lastly, before we call glDrawElements, we need to make sure our VertexColor attribute is pointing to our vertex colors in our vertex buffer object. A few changes are also made with our VertexPosition as well since we have added onto our array that contains the positions.
1 2 3 4 5 6 |
//set vertex data glBindBuffer(GL_ARRAY_BUFFER, VBO); glEnableVertexAttribArray(VertexPosition); glVertexAttribPointer(VertexPosition, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), 0); glEnableVertexAttribArray(VertexColor); glVertexAttribPointer(VertexColor, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat))); |
glEnableVertexAttribArray says that we want that attribute to point to this position in the vertex buffer for the enabled attribute. We need to do this for both VertexPosition and VertexColor so our shaders know where each specific piece of data is in the attribute array. With the call of glVertexAttribPointer(VertexPosition, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), 0); we say that vertex position has 2 elements that have the type of GL_FLOAT and don’t need to be normalized. The last two attributes specify the stride between each vertex position(in this case five GLfloat elements before the next vertex is reached) and the start position of the first element (0 since it is the first element). When we do it with VertexColor, the final element is (void*)(2 * sizeof(GLfloat) since the color starts two GLfloat elements after the first.