 So now we're going to look at texturing with example 4.1 when they run that here and you can see it's demonstrating how to paint a texture onto our two triangles and to bring in texturing We're going to need to be able to load images from files in this case from a jpeg and so for that purpose We're adding a new dependency as to be image now We also have these other two includes file system.h and shader s.h these provide abstractions for working with the file system for loading files and for Encapsulating a shader as a class. I'm not going to cover that. It's not anything specific to open gel It just abstracts around it in a little bit more convenient way And so now when we load a shader and set it up for use it's done with this constructor call Where you pass in the file path to the vertex shader and then the file path to the fragment shader And now those are stored in separate files Before we were just writing them in line as strings, but as your code gets larger, of course It's better to split them into separate files It's also advantageous this way because if we want to change our shader code without changing of our c++ code Now we don't have to recompile if I come over here and make a change to my shader code I just run the executable and because the shaders are loaded and compiled at runtime Then I don't have to rebuild the executable to change my shader code So with this exercise we've cleaned up the boilerplate a little bit Otherwise what's new here is now we're working with a texture And so now in our fragment shader rather than just taking a solid color and assigning to every pixel of our polygons We're going to read a color value from a texture and Two-dimensional textures in GLSL are represented with a type called sampler 2d That's going to be passed into the shader as a uniform Which we're going to have to set up in the c++ side of course But once we have our sampler we can sample from it We can read values from it with the texture function Specify which sampler and then the coordinate which here is an input variable that is going to be passed from the vertex shader A VAC 2 for a U and a V value for texture coordinates And note that this other input variable our color is being ignored in the shader We could just get rid of it It's a hangover from our previous exercise and I think it comes into play in a later exercise So that's why it's here. We don't really need it of course As for the vertex shader, it's getting three inputs now a third attribute for the UV coordinates of the vertices So a text cord here is for this vertex. What is the associated UV attribute? And we're creating two outputs that get passed as input to the vertex shader We set GL position as normal We're setting our color again like we did before and we're setting the output variable text cord I don't know why he's reconstructing a new VAC 2 value out of the X and Y of a text cord because it of course already is a VAC 2 So we should just be able to write This for the same result. I'm not sure why it was written that way. This way, of course will work So that's what's going on our shaders and now what we need to know is how do we set up on the C++ side? How do we set up this third input? Looking back at the C++ side, you see we have the indices for the two triangles because we're going to be using draw elements to draw this time We could of course just use draw arrays and not use indices But then we'd have to duplicate some of these vertices within the vertices array And it's just a little clearer if we don't have to do that So now we're adding in addition to the three position floats and the three color floats We now for every vertex also have two texture floats the U and the V So the top right gets one one the top right of her texture The bottom right gets one zero the bottom right of the texture The bottom left of a rectangle is mapped to the bottom left of the texture zero zero and the top left of our Rectangle is mapped to the top left of our texture So for these texture cords, we need to set up the third attribute with GL vertex attributed pointer index to an Attribute that will have a count of two values because it's just two floats not three this time There's still floats still don't want to normalize any fixed point values and The stride between vertices now is eight times the size of a float because each vertex has eight values Notice we have to update that for all the attributes and the offset to the third attribute is six floats in It's the sixth float within the vertex So that's our vertex attrib pointer call don't forget to enable the vertex attrib array with index two This should be a familiar thing by now We're now just setting up a third attribute as input for the vertex shader here coming in as a text cord But what about the texture? First thing we need to do is create a texture. So we do so with GL gen textures Much like we have done with other kinds of objects We then bind the texture so that certain calls that follow like this one here a text parameter I It's going to apply to this texture, which is currently bound And with these calls to text parameter I the I standing for integer to distinguish from calls where floats are accepted for parameter values We're configuring certain parameters of the texture including the GL texture up S value of the GL texture wrap T value Texture min filter texture mag filter. These are the most essential parameters What the texture wrap parameter is determined is what happens when you try and sample from a value That's out of the range of zero to one. Well here for the S and T dimensions Which correspond to you and V in different contexts We say you V for texture coordinates in other contexts. We say S and T, but they mean basically the same thing Anyway, we're setting the wrapping policy for both dimensions to be repeat such that say the you coordinate 1.1 would be interpreted as 0.1 2.1 would also be 0.1 3.1 will also be 0.1. So we repeat we're effectively ignoring everything But the fractional component of the value and this gives the effect of the texture wrapping and we can see what effect This would have if I were to change these values here. I'll change them to two save come over here and build the project Run the program and now you can see the texture is repeating four times What was just this lower left quadrant stretched over the whole rectangle now? It's that same thing repeated four times because of our wrapping policy of repeat There are several other options to repeat. I'm not going to go over them here So you can look those up But that's what's going on with the wrapping parameters as for texture min filter and mag filter Min as in minification mag is in magnification Min and mag have to do with mit map levels, which I discuss in a prior video and Linear here refers to bilinear filtering, which is again something I've explained in a prior video This is effectively is determining what happens when our UV cord does not correspond to the precise center of a texel to an individual color value in the texture and With bilinear filtering then the color you get is not a precise match for just that pixel It's a weighted average of that pixel and its immediate neighbors Again, this is something I explained in a prior video and there are many sources that explain the concept So I'm not going to delve into these details here Having said the texture parameters we need to load the actual data into the texture and so that's going to require reading the JPEG file Into an array of bytes and so that's what this STBI load function is doing and for this call It's going to set an int width and an int height and also the number of channels Though we're not actually going to use this in this case because we know what the number of channels in this image happens to be It's three. It's RGB Upon success data here will be non null. Otherwise, we're going to come down here and print out an error message But assuming we've successfully read the image then we're going to call tex image 2d The zero here specifies the mip map level here to find the texture for Again, mip maps are a concept I talked about in a prior video. I'm not really going to go into it here This RGB parameter is saying that the internal format what we're storing on the GP side It's going to be in the format of RG and B red, green and blue Then we have the width the height and the zero here is the width of the border for the image I'm not sure why you would give you textures borders, but that is an option The second RGB is specifying the format of the data we were reading from data and unsigned byte specifies the format of each value in this case they're unsigned bytes So with the texture data loaded into our texture object We call generate mip map to generate different levels of mip maps And I'm pretty sure this step is not required, but you do generally get better results. You get better text ring Anyway, now we have a texture object ready to use Last thing we should deallocate the data that was allocated by STBI This is no longer needed for a texture object because the data has been copied into the texture object Lastly within a rendering loop nothing here is new. Just make sure that the texture is bound before we draw Note here this method call on the shader object on the class encapsulating the shader The call to use here just sets the program as active by calling gluse program and very last thing here actually as well Having now created a texture object like everything else properly. We should clean it up before we exit So we should delete the texture Let's look at the next example 4.2 textures combined and Now what's happening is that we've loaded two textures and for each pixel. We're producing a combination thereof We're blending between colors of two textures for each point on our rectangle Our vertex shader is unchanged from the prior example But now the texture shader we have two uniforms two textures texture one and texture two And we're gonna use the mix function to get an interpolation between these two texture samples And we're doing so with the value point two meaning that the second one Contributes 20% and the first one contributes 80% and that is how we get the frag color So now that we're creating two textures We need two texture handles texture one and texture two and we do the whole process We saw before to create texture one With container dot jpeg and we just do the exact same thing for texture two with awesome face dot ping In this case though notice that it's RGB a there's an alpha channel both in the data We're reading from and the texture we're writing to we want to actually store that alpha channel because if we look at this ping You'll note that there are some actually transparent pixels not the whites of the eyes But everything around the the circle is transparent Now aside from creating a second texture the other thing we have to do differently now is we actually have to set the uniforms Strangely in the previous example We created a uniform variable and created that texture But we didn't explicitly set the sample to the uniform value to that texture Because what happened there is we only had one texture so implicitly in the shader. It couldn't have been anything else Here though we have two textures, so we have to set up the uniforms explicitly and So this called our shader that use that calls GL use program for our shader to make it active so we can set up its uniforms and Here for uniform variable called texture one. We're setting its value to zero and Here this is doing the same thing for texture two with a value one But here it's using a convenience method of our shader class to do so So now this means in our fragment shader texture one will have the value zero texture two of the value one So sample to these it really is actually just an integer value But it's an integer value representing one of the texture mapping units And so when we call texture and specify value zero it's saying we want to read from the texture mapping unit zero Now before we draw we have to make sure that Texture mapping unit zero is set to our first texture and texture mapping unit one is set to our second texture So that's what these lines do. We're saying make texture mapping unit zero the active texture and Bind texture one make texture unit one the active texture mapping unit and then bind our texture to handle If you're wondering well, what is the maximum number of textures I can use in any one shader? Well, that depends on your hardware and driver, but I believe the minimum required by the open gel standard is 16