In this post we will look at loading a texture and applying to the quad that we created in the previous post. The steps involved in this are:
- Load the texture as an image.
- Set up OpenGL to use our texture.
- Modify the Shader for Texture Mapping.
- Bind the Texture to GPU.
- Drawing the Quad with the Texture mapped on it.
Load and Set-Up the Texture
First we add a image file to the xcode project called texture.png (Drag Drop into a group called textures). Make sure that this file is added to the target so we can load it up.
We first start off by writing a function called setupTexture. This function will receive the name of the texture to be loaded and set-up and return the handle to the texture. We also need a Variable to hold the handle to our texture. For these we add the following lines to the GameMain.h header file.
//Texture Loading and Set-UP GLuint texture; GLuint setupTexture(NSString *filename);
Here is the setupTexture function that needs to go into the GameMain.mm class.
GLuint GameMain::setupTexture(NSString *filename)</pre> { CGImageRef spriteImage = [UIImage imageNamed:filename].CGImage; if (!spriteImage) { NSLog(@"Failed to load image %@", filename); exit(1); } size_t width = CGImageGetWidth(spriteImage); size_t height = CGImageGetHeight(spriteImage); GLubyte * spriteData = (GLubyte *) calloc(width*height*4, sizeof(GLubyte)); CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast); CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage); CGContextRelease(spriteContext); GLuint tex; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData); free(spriteData); return tex; }
This function is pretty straight-forward. We first load up the image and then read its data. Once we have the sprite data which is essentially an array of colors pixel by pixel in the texture, we create an OpenGL texture and bind this data to it. We then return the handle to this OpenGL texture resource.
In the GameMain class’s Initialize() function we call this function and assign the returned texture handler to the variable that we declared in the header. Add these lines to the top of the function:
/************Loading the Texture and configuring its settings*****/ //loading the texture texture = setupTexture(@"test_Scene.png"); //these settings are required to have non power of two textures loading // use linear filetring glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // clamp to edge glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Now that we have the image loaded up and set up as a OpenGL texture we will now modify the shader to receive and use this texture.
Modify the Shader for Texture Mapping
For the vertex shader to receive the UV co-ordinates for the vertices we need to declare an attribute which can be hooked to the buffer from the program. Since we will be using the UV coords to sample our texture in the fragment shader we will need a Varying variable.[Documentation on Varying Variables] We add the declaration of these variables to our vertex shader.
//the incoming vertex UV coords attribute vec2 texcoord; //UV coords to send to the fragment shader varying highp vec2 f_texcoord;
Since we dont need to manipulate the UVs in the vertex shader in any way. We just pass on the incoming UV coords to the fragment shader as is. So in the vertex shader function we add the following line.
f_texcoord = texcoord;
That is it for the vertex shader. Here is the pixel shader code.
//Texture uniform sampler2D mytexture; //incoming values from the vertex shader stage. //if the vertices of a primitive have different values, they are interpolated! varying highp vec2 f_texcoord; void main() { gl_FragColor = texture2D(mytexture, f_texcoord); }
The first line in the fragment shader is the important one. That is the declaration of the texture sampler to which we will be binding the texture from the program. We will also be setting the rules to this sampler for picking the color values. The rest is simple we have the incoming texture co-ordinates from the vertex shader since this is Varying the pixels between vertices will have interpolated UV coords. In the shader function we just sample the texture based on the UV coords and output the color value.
That is it for shader set-up, in the next section we will create the vertex buffer with the UV coords for vertices and hook this up to the shader.
Create the UV data
The explanation for the UV mapping is out of scope for this post. I will make a different series for the basics of things like vertices, UVs etc explaining how those work.
I will just go into the basics of UVs here. UV coordinates are a special co-ordinate system which generally run from 0 to 1. We can think of U being the axis along the width of the texture and V being the axis along the height of the texture. Every vertex that needs to be mapped with this texture lies somewhere on this texture and has a U and a V value. Since the coordinates are from 0 to 1 the resolution of the texture does not matter for the texture mapping the U and V value does not change. Having said that, using a very small texture for an object that occupies a big portion of the screen would make the texture look pixelated.
In the vertex data definition section of the Initialize() function of GameMain.mm we add the UV coords for the vertices along with the vertex positions.
/************Vertex Data Definition************/</pre> vector<float> geometryData; vector<float> uvData; //4 floats define one vertex (x, y, z and w), first one is lower left geometryData.push_back(-0.5f); geometryData.push_back(-0.5f); geometryData.push_back(0.0); geometryData.push_back(1.0); uvData.push_back(0.0f); uvData.push_back(1.0); //we go counter clockwise, so lower right vertex next geometryData.push_back(0.5f); geometryData.push_back(-0.5f); geometryData.push_back(0.0); geometryData.push_back(1.0); uvData.push_back(1.0f); uvData.push_back(1.0); //top left vertex is last geometryData.push_back(-0.5f); geometryData.push_back(0.5f); geometryData.push_back(0.0); geometryData.push_back(1.0); uvData.push_back(0.0f); uvData.push_back(0.0); //top right vertex is last geometryData.push_back(0.5f); geometryData.push_back(0.5f); geometryData.push_back(0.0); geometryData.push_back(1.0); uvData.push_back(1.0f); uvData.push_back(0.0);
Next we need to create a buffer for holding the UV data just like we did for the vertex positions data. First we add the buffer’s declaration in the header file.
//buffer ID for the color data in the video memory unsigned int m_UVBuffer;
Next we generate the buffers in the Initialize() of the GameMain class.
//generate an ID for the color buffer in the video memory and make it the active one glGenBuffers(1, &m_UVBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_UVBuffer); //send the data to the video memory glBufferData(GL_ARRAY_BUFFER, uvData.size() * sizeof(float), &uvData[0], GL_STATIC_DRAW);
We now have the UV data stored, next we need to hook this up to the shader and start drawing the quad.
Drawing the Textured Quad
First we have to get the locations of the UV attribute and the texture sample just like we did for the position data while drawing the Quad in the previous post. We start off by declaring the handles to the location in GameMain.h
//location of the texture sampler and the uv attribute int m_textureLocation, m_uvLocation;
Next we query the shader for the locations and set them to the handles in the Initialize() function.
m_uvLocation = glGetAttribLocation(m_shader->getProgram(), "texcoord"); m_textureLocation = glGetUniformLocation(m_shader-> getProgram(), "mytexture"); //check that the locations are valid, negative value means invalid if(m_positionLocation < 0 || m_uvLocation < 0 || m_textureLocation < 0) { fprintf(stderr,"Could not query attribute locations"); }
That’s it. We now have all our initializations complete for the textured drawing. Next we have to modify the Draw() function to use the texture and draw the quad.
void GameMain::Draw() { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glUniform1i(m_textureLocation, /*GL_TEXTURE*/0); glEnableVertexAttribArray(m_positionLocation); glEnableVertexAttribArray(m_uvLocation); //bind the geometry VBO glBindBuffer(GL_ARRAY_BUFFER, m_geometryBuffer); //point the position attribute to this buffer, being tuples of 4 floats for each vertex glVertexAttribPointer(m_positionLocation, 4, GL_FLOAT, GL_FALSE, 0, NULL); //bind the uv VBO glBindBuffer(GL_ARRAY_BUFFER, m_UVBuffer); //this attribute is only 3 floats per vertex glVertexAttribPointer(m_uvLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); //initiate the drawing process glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableVertexAttribArray(m_positionLocation); glDisableVertexAttribArray(m_uvLocation); }
Right at the start of the function we tell the OpenGL pipeline that we have a texture to draw and then we set the handle to our texture at the texture0 location for drawing. we then enable the UV attribute and draw the quad as usual.
build and run this now and you should be able to see the quad with the texture applied to it. Here is a screenshot of the output.

You can grab the project from the GIT repository tagged vOGL_2D.005.
In the next post we will implement the world and projection matrices so we can have the plane to move and rotate.