Learnopengl. Lesson 2.4 – Texture maps / Habrahabr

September 10, 2017

 image "1945 = 1945" </p>
<!-- WP QUADS Content Ad Plugin v. 1.5.2 -->
<div class=

Previously, we discussed the possibility of each object to have a unique material to respond differently to light, which is great for giving each object a unique look relative to other objects on the stage, but this still does not give us much flexibility in adjusting the appearance of the object.

In the previous lesson, we defined the material for the whole object, but in the real world, the objects usually consist not of one but of one Imagine a car: its outer shell is shiny, the windows partly reflect the environment, the car also has matte tires, and there are also sparkling rims (sparkling if you wash your car well.) So, each object has different material properties

So, our material system from the previous lesson is not suitable for more or less complex objects, so we need to expand it by introducing a diffuse and glare ] card. This will enable us to influence the diffuse (and, indirectly, the background, as it is almost always the same) and the glare component of the object with greater accuracy.

Diffuse maps

All we need is a way to set a diffuse color for each fragment of the object. What can affect the value of the color, based on the position of the fragment?

Remember? These are textures, which we intensively discussed in one of the previous lessons. Illumination maps are just another name for the same principle: using an image applied to the surface of an object, we can make color samples for each fragment. In scenes with lighting, this is usually called a diffuse map (as a rule it is so called 3D artists), since the texture image represents all diffuse colors of the object.

For demonstration of diffuse maps we will use image of a wooden container with an iron frame:

Using diffuse maps in shaders is very similar to using textures in one of the previous lessons. However, we will now replace the previously determined vector vec3 diffuse color diffuse map sampler2D .

Keep in mind that sampler2D is the so-called opaque data type . This means that we can not create a copy of this type, we can only define it as uniform . If we try to use this type not as uniform (for example, as a function parameter), then GLSL will display strange errors. This rule also applies to any structure that contains an opaque type .

We also removed the vector ambient since in most cases coincides with a diffuse color, so we do not need to store it separately:

  struct Material {
    sampler2D diffuse;
    vec3 specular;
    float shininess;
in vec2 TexCoords;

If you are so stubborn and still want to set the background color different from diffuse, you can leave the ambient vector. But the background color will be one for total object. To get different background colors for each fragment of the object, you need to use a separate texture for the background color values.

Note that we again need texture coordinates in the fragment shader, so we declare additional incoming variable. Then we just make a selection from the texture to extract the value of the diffuse color of the fragment:

  vec3 diffuse = light.diffuse * diff * vec3 (texture (material.diffuse, TexCoords));

Also, do not forget to set the background color of the material the same as diffuse:

  vec3 ambient = light.ambient * vec3 (texture (material.diffuse, TexCoords)) ;

This is all we need to use a diffuse map. As you can see, there is nothing new in this, but it gives an impressive increase in visual quality. For this to work, we need to add texture coordinates to the vertex data and pass them as a vertex attribute to the fragment shader, load the texture and associate it with the corresponding texture block.

Updated vertex data can be found here. Now they include the vertex positions, normal vectors and texture coordinates for each vertex of the cube. Let's update the vertex shader so that it can take the texture coordinates as the vertex attribute and pass them to the fragment shader:

  #version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
out vec2 TexCoords;

void main ()
    TexCoords = aTexCoords;

Make sure to update the vertex attributes of both VAOs : refer to the VAO textured cube and the VAO cube lamp ) so that they match the new vertex data, and loaded the image of the container into the texture. Before we draw a container, we need to assign the preferred texture block to the variable material.diffuse and associate the texture of the container with it:

  lightingShader.setInt ("material.diffuse", 0) ;
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, diffuseMap);

Using a diffuse map, we again received a huge increment of detail and now, with the added lighting, our container really started to shine (literally). Probably now it looks like this:

You can find the full source code of the application here.

Blizzard maps

Probably , you noticed that the highlight looks a bit strange, because our object is a container, which mostly consists of wood. And, as we know, the tree does not give such a mirror shine. We can fix this by setting the vector specular in the structure Material to vec3 (0.0) but this means that the iron frame of the container will also stop reflecting, and we we know that metal should at least a little shine. Again, we would like to control which parts of the object should shine and with what force. This problem is very similar to the discussion of diffuse maps. Coincidence? I do not think so.

We can again use the texture map, only now for specular highlights. This means that we need to create a black and white (or colored, if you want) texture that will determine the strength of the shine of each part of the object. Here is an example of a glare map:

The brightness intensity is determined by the brightness of each pixel in the image. Each pixel of such a map can be represented as a color vector, where the black color is vec3 (0.0) and the gray one – vec3 (0.5) for example. Then, in the fragment shader, we select the corresponding color value and multiply it by the intensity of the highlight color. Accordingly, the "whiter" the pixel, the greater the result of multiplication, and, consequently, the brightness of the glare on the fragment of the object.

Since the container is mostly made of wood, and the tree is a material that does not give highlights , then the entire "wooden" part of the texture is blackened. The black parts in general do not shine. The surface of the steel frame of the container has a variable force of mirror luster: the steel itself gives quite intense glare, while cracks and scrapes do not.

Technically, the tree also has mirror reflections, although with (the light dissipates more strongly), but for educational purposes, we will pretend that the tree does not react to the mirror light.

Using tools such as Photoshop or Gimp it's pretty simple to turn a diffuse texture into a blu Ikova. Simply cut out some parts, make the image black and white and increase the brightness / contrast.

Sampling glare maps

Map of mirror images is the most common texture, so the download code is very similar to loading a diffuse map. Make sure you upload the image correctly and generate a texture object. Since we are using a new texture in the same shader fragment, we need to use a different texture block for the glare map. Let's link this texture to the corresponding texture block before rendering:

  lightingShader.setInt ("material.specular", 1);
glActiveTexture (GL_TEXTURE1);
glBindTexture (GL_TEXTURE_2D, specularMap);

Then we update the material properties in the fragment shader so that the reflecting component is accepted as sampler2D and not vec3 :

  struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float shininess;

Finally, we need to sample the glare map to get the corresponding glare intensity for each fragment of the object:

  vec3 ambient = light.ambient * vec3 (texture ( material.diffuse, TexCoords));
vec3 diffuse = light.diffuse * diff * vec3 (texture (material.diffuse, TexCoords));
vec3 specular = light.specular * spec * vec3 (texture (material.specular, TexCoords));
FragColor = vec4 (ambient + diffuse + specular, 1.0);

Using a glare map, we can determine with an extreme accuracy which parts of the object give specular highlights and set their corresponding intensity. Thus, the glare map gives us an additional level of control over the diffuse map.

You can also use colors in the glare map that determine not only the intensity of the glare, but also its color. However, in reality, the color of the highlight is largely (and in most cases completely) dependent on the light source, so the use of color glare maps will not give realistic results (which is why these images are usually black and white – we are only interested in the intensity of the glare).


If you run the application, you will see that the container material is very similar, to a wooden container with an iron frame:

You can find the full The source code for the application is here.

Using diffuse specular maps, we can add a huge amount of detail in a relatively simple objects. We can add even more details using other texture maps, such as normal / relief maps and / or reflection maps, but we'll save them for the next lessons. Show your container to friends and family and remember that one day our container can become even more attractive than now!


  • Try to play with the background, diffuse and glare vectors of the light source and see how they affect the appearance of the object.
  • Try to invert the colors of the highlight map in the fragment shader, i.e. the tree should shine, and the iron frame – no (note that the cracks on the frame still give a mirror image, albeit with less intensity): the solution.
  • Try to create a glare map from a diffuse map that does not use black and white colors, and you'll see that the result is not very realistic. You can use this color glare map if you can not make it yourself. Result:
  • You can also try to add an emission map to our cube which stores the glow value of each fragment of the object. Emission values ​​are the colors that an object can radiate as if it contained a light source. Thus, the object can be illuminated regardless of the lighting conditions. Usually glow maps can be seen on those gaming objects that are shining (for example, the eyes of a robot or a strip of light on a container). Add this texture (from creativesam) to our container as a glow map, so that the letters emit light. The solution, the result.

Leave a Comment

Your email address will not be published.