 Now that we've covered the basics of lighting, let's bring texturing into the mix and here what we see is a textured box that actually has two textures applied, one which we call the diffuse map and one the specular map, because the diffuse map its base color for each pixel is what we use in the diffuse lighting calculation and the specular map its base color at that fragment is what we're using for the specular calculation. So effectively can have on a per pixel basis a totally different color used in the separate lighting calculations and so you can see here that our specular highlights are only shown up for what should be this metallic rim of this object to make sense because the base material is supposed to be wood but with a metallic rim and we're getting no specular highlight on the wood itself because if we look at the textures the specular map on the right is entirely black except for the metallic rim around the side and so by having these two separate textures, these separate maps, we get per pixel control of the base color used in these two separate lighting calculations which makes sense for certain objects like this mix of wood and metal because even on the metal you notice that there are some blacker portions darker portions and those effectively are scuff marks. So looking first at the fragment shader it's just like our prior example except now we're putting a texture coordinate back into the mix and now if we look at the fragment shader our material is defined by two separate textures two sample 2Ds one for the diffuse one for the specular and so when we do our diffuse calculation and also for that amount of the ambient calculation we are reading from the diffuse texture but then for the specular calculation the base color is from the specular map. On the C++ side there's really nothing new here just note that we're setting effectively two textures so for diffuse we're setting that to zero for a specular material we're setting it to one and then we just need to make sure to bind the diffuse texture to the first texture unit and the specular texture to the second texture unit. Looking now at 5.1 light casters directional what's different here is that our scene is lit by what's called a directional light rather than a point light. A point light as the name implies is a single point in space from which light is emanating in all directions but the idea of a directional light is that you have essentially a whole field of light rays all running in parallel pervading your whole scene so a directional light is not defined by any position just merely a direction. A directional light is the most obvious approximation of sunlight because the sun is so far away from the earth that even though the light rays are not literally running in exact parallel they come from such a distance that by the time they reach the earth that they mostly are running in parallel or close enough that we can't tell the difference and so a directional light is going to be your primary light source in an outdoor scene at least in the daytime but it also is sometimes useful to use a directional light in interior spaces generally larger ones but anyway what this looks like in code is that for our fragment shader our light now doesn't have a position anymore that's been commented out it just has a direction of that three still and the only other difference is that when we compute the light direction it's not the vector from the fragment position to the light position because there is no light position is just simply the light direction reversed. Direction here is expressed as the direction light is traveling for diffuse and specular calculations we want the vector going the other way so we make this negative and that's actually all the changes here. Note that we don't need frag position for the diffuse calculation anymore but it is still involved in the specular so we can get that angle from the point to the camera the view direction here. For the next example 5.2 light casters point we've gone back to a point light but now our light is attenuating it is decreasing in intensity over distance you can see in the back those those boxes back there they're much darker than these ones up front. So you might assume that our light should attenuate it should fall off in linear proportion to the distance but it turns out for a more realistic look for something that simulates how light tends to behave in typical environments is we want the attenuation governed by three values one we just call the constant here a linear coefficient and a quadratic coefficient and what's happening down here is our attenuation factor is going to be one over this combination of those constants with the distance so we just have the constant by itself added with the linear times the distance plus the quadratic times the distance squared that's why it's called the quadratic because it's multiplied by the distance squared and so this all adds up together into a value that generally should be greater than one it really wouldn't make sense to be less than one because if it were less than one our attenuation factor would be greater than one and we multiply it into our ambient diffuse and specular it would make them brighter not dimmer so generally we make this constant something greater than one because we don't want attenuation to make the light brighter. As for the linear and quadratic values typically quadratic is going to be smaller because the kind of attenuation curve we want is something where the light decreases at a relatively slow and steady rate until we get to a certain distance and then you want it to start falling off rather quickly that is the effect we're generally going for because that is how lights will typically behave in real environments so in this case for example our linear is 0.09 and our quadratic is 0.032 and notice our constant is one here. In example 5.3 light casters spot now our light is a spotlight it's a spotlight which in this case is attached to the camera like a flashlight and always pointing in the same direction of the camera and there's a certain cutoff angle there's a cone of light outside of which the light is not being applied you can see the pixels outside of the cone are still getting some ambient light but they're not getting any diffuse or specular lighting from the spotlight so what's new here other than our light is now traveling along with the camera that's a fairly obvious thing to do just update its position to always match the position of the camera but for a spotlight it also has a direction and it has an angle a cutoff angle such that if the vector from the light to the frag position to the eliminated point if the angle between that vector and the direction vector of the spotlight when that angle exceeds the cutoff then that fragment gets no lighting at all from the spotlight so we get this hard cutoff it's a very hard-edged light here so in our c++ code for the light we're setting this position to be the same as the camera we're setting this direction to be the same as the camera and we now have this cutoff which here is 12.5 expressed as radians and we get the cosine of that and we want the cosine not the actual radians or degrees because in the fragment shader when we compute the angle it's by getting the dot product and the dot product is equal to the cosine of the angle so looking at the fragment shader we get the light direction just like for any point light we get its dot product with the direction of the spotlight though reversed this is a little strange actually we're reversing the direction of the spotlight itself not the light direction you could do it the other way around we could negate light durr instead of the direction of the spotlight itself either way we'll get the same result we store as theta and if this theta is within our cutoff then we're in the bounds of the cone of the spotlight remember that for cosines when the angle is zero the cosine is one and as the angle increases the cosine shrinks so that's why this is a greater than test not a less than for smaller angles the cosine is larger so if this condition is true then we do our diffuse and specular calculations and attenuate them notice that in this example we're not attenuating the ambient because in this example that's not the effect we're going for we want to be able to see those boxes just a little bit even if they're in the distance and outside of our spotlight in the next example 5.4 light casters spot soft you can see that our spotlight now doesn't have a hard edge it fades from full intensity to blackness at the edge so what's going on here is now in addition to the cutoff we have an outer cutoff of some larger degree in this case 17.5 degrees and if we look at the fragment shader rather than making the diffuse and specular calculations conditional instead we're computing this intensity value and the way this will work out is when theta here is within the inner cutoff then the intensity will be one when it is outside of the outer cutoff it'll be zero and then for everything in between it'll transition between zero and one from the outer it starts at zero and as it works towards the inner it increases to one i won't walk you through the calculation here but you can study it and see that's what's happening and so again the intensity will be one as long as we're inside the inner cutoff it'll be zero when we're entirely outside of the outer cutoff and in between it transitions from zero to one in a linear fashion creating that soft edge effect for a last lighting example we now have multiple lights combined together into one scene there are multiple point lights as you can see from each of the boxes there are four it looks like yes and we have a directional light lighting up the whole scene pervasively and we also have a spotlight so now in the fragment shader we have separate starts for directional light point light and spotlight and for our uniforms we have one directional light one spotlight and an array of point lights so now because the code for this fragment shader is getting a little verbose it's been split up into separate functions but if we just look at our main here first we're calculating the directional light and then for each of the point lights in this loop we add onto result calculating for each point light and then lastly we add on to the result what we get from calculating the spotlight and we get one resulting frag color in the end and if we look at these three functions it's the same logic we've saw before just now split off into their own functions so i'm not going to walk you through it let's look at this example which loads a complex model from a file notice it takes a little while to load because it's reading from the file and now it's displaying this crisis character so what's going on here is under the resources directory we have this nano suit model which is defined by this object file and if we open it up it is actually just a text file you can see that it's a bunch of vertices lines starting with v nothing really surprising there vt these are texture coordinates vn's those are normals f's to find faces as combinations of indexes so very much like the indices we saw with draw elements in open gl and the single file is logically split into separate objects here one called visor lines beginning with o and then there's some other objects if we scan down where are they i should search for o space yeah they're legs yeah so this single model is actually logically split into sub models and note also at the top it references this material file which is in the same directory and that describes for each of the objects the arm body glass hand helmet leg what images are used for the textures for the speculative lighting for the diffuse map and for the normals and those again are in the same directory so now in the example's code we're bringing in this new dependency of this model class to find in model dot h and these models can logically be comprised of multiple meshes where each mesh is its own logical component like we saw in the file and this model class itself is bringing in a third party dependency the ascent library asset import library which can read in parts many different model file formats including of course obj and so i'm not going to go through all this code just note that for the model is keeping track of all the textures loaded and when it loads them it actually uses stp image to read them off of disc and create an actual open gl texture with an associated texture handle for each one of the meshes when the model itself is drawn the meshes are drawn individually and for each one there is a separate draw call there's a separate draw elements call because each mesh has its own vao it has its own array of textures and all the vertices and the indices and when the mesh is constructed all this data is packed into the appropriate types of open gel buffers like we've already done before in our examples open gel wise is nothing new for us so i'm going to let you study the code yourself if you're interested i will point out one new thing this model loader supports vertex attributes we haven't seen before what are called tangents and by tangents these are related to normals and they're used in conjunction with normal mapping which we'll see in a later example