Goals
- Learn about applying 2D texture maps.
Resources
Before you being, read these:
- Texture mapping is described starting on p. 368 of your textbook.
- A good summary of all of the details surrounding textures can be found on the openGL wiki.
Implementing a Procedurally Generated Checkerboard Texture
- Make a copy of your completed car project from week 8.
- Add a texture coordinate attribute variable to each of your shape objects. (see Texture Coordinates section below)
- Download the files Textures.cpp and Textures.h from the fileshare. Add them to your project. The methods in the Texture class do the following:
- makeCheckerboard(): Creates the texture data stored in the “image” variable.
- setUp(): Creates, loads, and sets parameters for the texture object. To make sense of what it is doing in setUp(), read the comments in the code and also read the description on the openGL wiki.
- bind(): binds the active texture unit to this particular texture. We don’t need to use this just at the moment. Note, we currently only use one texture unit (unit #0) and one texture (the checkerboard). In the next section, we will see how to use bind() to choose between multiple textures (e.g. checkerboard and image).
- In the application program (main.cpp), create a Texture object (from the Texture.cpp class) and call setUp(program) from the init() method (after “program” is already created).
- Modify the vertex and fragment shaders (see Shaders section below).
Texture Coordinates
Modify the code for each of your objects (Cube, Cylinder, etc ) to include a new attribute variable for texture coordinates:
- In each .h file: Add a new array of vec2 variables, e.g. called tex_coords.
- In each cpp file:
- Create the tex_coords array.
- Load the array: For each vertex, set the value for the tex_coords, along the lines of how you set the values for the points, normals, and colors,
- In the createVAO method, you need to add a “vTexture” attribute variable. Be careful, the tex_coords array isn’t the same size in bytes as the points array.
- Don’t forget to delete the array in the destructor.
Shaders
The instructions below assume you are using the Phong shader (as opposed to the Gouraud shader), so that your color calculations are done in the fragment shader.
The Vertex Shader:
- The texture coordinate attribute variable (e.g. vTexture) must be passed as an “in” variable into the vertex shader (attribute variables can’t be passed to the fragment shader). This variable will need to be interpolated (by the rasterizer) and passed on to the fragment shader. Therefore, you need both an “in” and an “out” variable:
in vec2 vTexture; // in from the attribute variable out vec2 texCoord; // out to the rasterizer and fragment shader
- Somewhere in the shader’s main() method, you need to make the connection between these two variables:
texCoord = vTexture;
The Fragment Shader:
- You need an “in” variable to store the incoming texture coordinate passed in from the vertex shader.
in vec2 texCoord;
- In GLSL, a “sampler” is a uniform variable that represents a texture. Texture objects are not directly associated with or attached to program objects. Instead, program samplers reference texture image unit indices (remember, we associated our texture with GL_TEXTURE0 – see the setUp code – and then linked it with the uniform variable name “texture” in the shader). Therefore, you need to declare a uniform sampler in order to access the texture:
uniform sampler2D texture;
- To obtain the value of the texture at the specific texture coordinate, you use the texture2D method. Assuming you want to combine the lighting computation with the texture, replace the line:
vec4 myColor = model_color;
with the line
vec4 myColor = texture2D( texture, texCoord );
or, if you want to combine the texture with the model_color:
vec4 myColor = model_color*texture2D( texture, texCoord );
Car with lighting and texture.
Car with model_color combined with texture.
Car with black elements discarded.
Implementing an Image Texture
To use images as textures, you need to be able to read images into your c++ code. We limit ourselves to tga files because they are easy to read. You may use Photoshop (or other program) to convert any images you have to this format.
- Download the files from imageCode, or the imageCode.zip from the fileshare and place the files into your project. It contains the files
- TargaLoad.cpp and TargaLoad.h: reads in tga files
- ImageTexture.cpp and ImageTexture.h: This class is very similar to the Texture class from the previous section except that instead of creating a texture (checkerboard), it uses the TargaLoad.cpp class to read in a tga image file and set it as an openGL texture.
- test.tga: a sample tga file that looks like:Feel free to create your own image textures.
- Just as you did in the previous section with the Texture class, create an ImageTexture object in the application program (main.cpp), and call setUp(program) from the init() method (after “program” is already created):
ImageTexture myImageTexture("test.tga");
Don’t get rid of your Texture object; you will be using both the checkboard and the image.
- Just before you draw an object, you can set which texture to use by calling bind():
myImageTexture.bind(program);
- If everything is black when you run the program with the image texture, it may be that it is not finding the tga file. An error will be generated on the console if this is the case. The reason it may not find the file is because you do not have the project’s execution directory set properly. Go to the menu: “Project->Properties…”, then click on the tab “Build Targets”, and make sure the “Execution working dir” is set properly.
- Of course, you generally don’t want everything to have the same texture. For example, you may want one shape to use the model_color, another to have a checkerboard, and another to have an image. It is simple to switch between different textures. In your code, you have created two different texture objects, a checkerboard and an image. All you have to do is to bind the desired texture just before you are about to draw a shape. For example, if my image texture object is called myImageTexture, then you add the line:
myImageTexture.bind(program);
- If you want some shapes to use the model_color and others to use vColor or textures, then it is more complicated. One option is to create different shader programs. However, in this case, we are only wanting to change one line in the shader code so it makes more sense to introduce a new uniform variable that is a flag indicating whether one wants to use model_color, vColor, or textures. The image below shows 3 cars, one is drawn with the image texture, one is drawn using the model_color, and one is drawn using vColor. The ground is drawn with the checkerboard texture.Add a uniform variable so you can choose between these options.
No later noon Week 10, demonstrate your final program (the one with different textures and coloring) in lab and submit this code to the fileshare.