 So, now we're going to look at Exercise Hello Triangle Indexed, which is 2.2, and if we run it, it's actually not rendering a triangle, well, it's rendering two triangles that form a rectangle. And while we could render two triangles just like we rendered a single triangle before, this time instead of calling glDrawArrays, we're calling glDrawElements. And what drawElements does differently is that, yes, it expects a VAO, which we have set up, it expects a shader program, but it also expects a buffer of indices. The indices are indexes into the vertex data, such that we construct our primitive, in this case a triangle, by specifying three indices. So if we look up at our vertices array, we're going to end up forming two triangles, yet we're only specifying four vertices. And then in our buffer of indices, we're going to have first a triangle made up of index zero, one, and three, meaning this vertex, this vertex, and then the one at index three. And then the second triangle is formed from one, two, and three, so it's formed from these three vertices. Without these indices, if instead we were using glDrawArrays, then here we would have to have six vertices, three for the first triangle, three for the second. And now it would mean that the vertices at indexes one and three, they'd have to be repeated in the vertex data. That may not seem so bad with a small set of vertices, but if you imagine for a much larger model, you're going to typically have many, many more shared vertices that are going to have to be stored redundantly. And also the problem is that as we'll see later, our vertices are going to be accompanied by attribute data. So each vertex might end up being something significantly more than just three floats. It might end up being 12 floats or beyond. So it's an even greater savings than if we don't have to store redundant vertices. So now, as I said, everything in this example is exactly the same, except now we're going to have something you might call an element buffer object, which will have the indices data, and this has to be set up before we call glDrawElements. So here, when we create our VBO and our VAO, nothing is changing there, except now we also have another buffer for EBO. We're binding the VAO just like before, setting up the VBO just like we did before, same a trip pointer setup, that's not changed at all. But now for EBO, we're going to call glBind buffer, this time specifying type element array buffer, not regular array buffer, as we do for our VBOs. And having bound this buffer, we then want to put the indices data into it. It's just like we do with buffer data here for the vertices, except again here you're specifying element array buffer, not a regular array buffer. And so now the EBO is actually ready to go. We don't associate it with a VAO the way we do with the VBO. VAOs do not make reference to these element buffer objects. Instead, it's simply the case that when we call glDrawElements, whatever is currently buffer bound with glElementArrayBuffer, that is understood by draw elements to be where the indices are specified. In fact, if we don't have EBO bound at this point, if there is no currently bound element array buffer, then this call would crash. Note that we're specifying of course the type of the primitives. This is the number of vertex indices that are going to be read. This is the type of the indices, not the type of the vertices themselves, the type of the indices, they're unsigned ints. And the zero here is an offset, so you can have it read from some index other than the first index. So that's all that's different here, except one nice thing. We created the EBO, so we should also delete it like any other buffer when we're done. So we call deleteBuffers down here with EBO. So now let's look at exercise 3.1, shaders uniform, and here it's rendering a triangle, but if we wait a moment you'll see that it's actually changing its color pulsing slowly over time. The first thing that's different here is in our fragment shader, we now have this variable of type vec4 called ourColor and as it says it's a uniform variable. Uniforms are variables in our shader program that are global throughout the program and in fact are not allowed to change in the running of the program. And before we run the program, before we call drawArrays or drawElements, we have to set up the values for the uniforms first. They're basically inputs from outside the shader program. And be clear when we say they're unchanging during the run of the program, that's the shader program which runs once each frame. So frame to frame in this example we're changing what the ourColor value is and as you can see in the code it's simply taking whatever the ourColor is from the uniform and setting that to be the frag color. Looking down at the loop, we've actually in this case gone back to drawArrays, we're not using drawElements and we're not using indexes anymore. We could but there's no reason to do so so we're just going back to the more straightforward method in this case. But before the draw call, we're calling glUniform4f. 4f here means a vector of four floats because OpenGL is codified for C and C doesn't have function overloading. It's the case that in OpenGL you have a number of functions where there are many variants for different kinds of input. So this is the version of glUniform that takes in a vector of four floats, which are these four float values here. And when you call glUniform, the shader program whose uniform we are setting has to be the currently enabled program so we have glU's program up here for our shader program but the call also has to specify which uniform variable we're setting the value for because there could be many. So that's why here you're specifying vertex color location which is just an int value that has been defined up here by call to get uniform location passed in the shader program and the name of the uniform variable which in this case is our color. And be clear that uniform location actually is not changing frame to frame so this could just be done outside the loop. So that's how we set up the uniform value and in this specific case the uniform is being used as a color value where the red is zero, the blue is zero, the alpha is one, and the green value is going to vary frame to frame based on the current time which we're getting from gl of w and then using a sine function to have it vary in a in a pulsing gradual transition way. Sine of course always returns something in the range of negative one, the positive one, and so frame to frame the green value is pulsing up and down from black to a certain green shade in a smooth curve. Now I did say that uniforms are not meant to be mutated in the course of the shader program so let's actually see what happens if we do try and mutate the uniform here I'm going to modify our color just set it to this white value save I'm going to come over here and build the project run the program and well it didn't crash the program but you can see in the console we have some errors now there's first a compilation failure and then of course because it failed to compile we also can't link and it's telling us that there's an assignment to uniform our color and you're just not allowed to assign to uniform so that's why that's invalid. So the compiler caught the error. Now let's look at example 3.2 shaders interpolation I'll run it and you can see we get a pretty rainbow triangle so what's going on here well firstly looking at the shaders whereas before we just had the one input to our vertex shader now we have a second input notice it says location one this is going to be a color value as the name indicates also a vec3 also now in addition to assigning to gl position which is just an automatic variable that is created implicitly we don't have to create it ourselves it's always there we're now creating another output variable called our color vec3 which is going to be passed down the pipeline so if we had geometry and tessellation shaders we could get this value there but we don't have such shaders we do though of course have our fragment shader and so our fragment shader needs to accept as input now our color and I believe the fact that the name and type matches is significant I'd have to test this and look at the docs I can't say any reason why you'd make them not match it otherwise I think might have to do with the ordering so if we had multiple output variables multiple input variables that they may just match up in order I think it might work that way instead actually I'd have to check anyway as we have it configured here works output of our color is going to be accepted as input here and now in the fragment shader when we set frag color we're setting it to the our color input but understand in the output shader for each vertex we're spitting out a different color and for a triangle then the question is well what does the fragment receive for a pixel within a triangle which of those colors do we get well what we get is the interpolation thereof the perspective correct interpolation as we discussed in earlier videos in the whole rasterization process the hardware is figuring out how to interpolate these values in a perspective correct way and that's what we get here as input to our fragment shader so aside from the shader's being different what's different in a rendering loop well the rendering loop is nothing new here it's just like our actually earliest example we're just rendering the triangle as currently configured by the VAO no indices this time again we're just using draw arrays so nothing new here but if we go look at our vertices array now it contains not just positions but colors and notice the layout where it's for each vertex it's first the position the three floats followed by three rgb floats for the color and whereas before we just had one vertex a trip pointer call specifying what the vertices are and how they're laid out in the array now we're going to have a second vertex a trip call notice it says index one specifying how the colors are laid out and this top call actually also changes because where it was here three times size of floats for the stride because the positions were just spaced three floats apart now there's six floats apart it's from here to here to the next one right well same thing for the color attributes they are spaced six floats apart here to here is six floats but we also need to specify an offset that starts here because this is where our first color is of course so that's why this is a void pointer of three times size of float not zero and do not forget to call enable for text a trip array otherwise it would not go into effect so having set up uh both the indexes of vio index zero and index one for the vertex data itself and then the attribute of the color when the shader gets called it's going to receive two inputs one at location zero as an index zero of the vio and the other at location one index one of the vio which is our colors so again understand the relationship between the vio and the vbo or vbos is that the vio is a structure of i believe on most open gel limitations the the max is capped at 16 i believe that's usually the case so you can have up to 16 indexes for your vio potentially pointing to different vbos but in this case it's just one and for each index in the vio and also has information about how the data is laid out within the buffer that's why when we come down to the draw call we don't need to have the vertex buffer object bound we just need the vertex array object bound because it internally has references to any relevant vbos and i believe actually in the prior video i may have said it was safe to delete the vbo before this call um well i think we got away with it in that prior example because there aren't any subsequent allocations on the gpu side that might potentially overwrite the memory that vbo was occupying but in a larger program it might be the case that if you delete the buffer i believe just like when you might manually free memory on the cpu side the danger is that that chunk of memory might subsequently get overwritten by later allocations so while the data of this vbo is in use even though we don't need to have the vbo bound i believe it's the case that it's not safe to delete it so we should only delete the vbo here when we clean up so let's run the program again and see uh this top vertex this was set to the color of rgb zero zero one as in full blue this here was uh zero one zero as in full green this was one zero zero as in full red and then for the fragments in between for the pixels in between it's interpolating between those color values that's why it smoothly transitions and you get the full colors of the rainbow all in this one triangle