OpenGL ES 2.0 for iPhone Tutorial Part 2: Textures
In this tutorial series, our aim is to take the mystery and difficulty out of OpenGL ES 2.0, by giving you hands-on experience using it from the ground up! In the first part of the series, we covered the basics of initializing OpenGL, creating some simple vertex and fragment shaders, and presenting a simple rotating […] By Ray Wenderlich.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
OpenGL ES 2.0 for iPhone Tutorial Part 2: Textures
25 mins
Repeating Textures
In OpenGL, it’s nice and easy to make a texture repeat over and over across a surface if you’d like. The stone tile we’re using happens to be a seamless texture, so let’s try repeating it several times across each face.
Simply make the following change to OpenGLView.m:
#define TEX_COORD_MAX 4
So now we’re mapping each cube face so the bottom left is (0,0) and the bottom right is (4, 4).
When mapping texture coordinates, it will behave as if it was a modulo of 1 – for example if a texture coordinate is 1.5, it will map to the texture as if it was 0.5.
Compile and run, and you’ll see the texture repeating nicely along the cube!
Note: This works automatically because the default value of GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T are set to GL_REPEAT. If you don’t want textures to repeat like this (maybe you want them clamped to the last pixel value), you can override this behavior with glTexParameteri.
Adding the Decal
We’ll wrap this tutorial up by putting a small fishbone on top of this cube. Why? Because the Grand Cat Dispatch got hungry!
The code to do this is mainly just extra practice for what we’ve done already in this tutorial series. So let’s jump right in.
Open up OpenGLView.h and add the following new instance variables:
GLuint _vertexBuffer;
GLuint _indexBuffer;
GLuint _vertexBuffer2;
GLuint _indexBuffer2;
Before we only had one vertex and index buffer, so when we created it we just bound it as the active buffer and never needed a reference to it. We’re going to need two vertex/index buffers now (one for the cube, and one for the face that will hold the fishbone decal), so now we need some references.
Switch to OpenGLView.m and make the following changes:
// 1) Add to top of file
const Vertex Vertices2[] = {
{{0.5, -0.5, 0.01}, {1, 1, 1, 1}, {1, 1}},
{{0.5, 0.5, 0.01}, {1, 1, 1, 1}, {1, 0}},
{{-0.5, 0.5, 0.01}, {1, 1, 1, 1}, {0, 0}},
{{-0.5, -0.5, 0.01}, {1, 1, 1, 1}, {0, 1}},
};
const GLubyte Indices2[] = {
1, 0, 2, 3
};
// 2) Replace setupVBOs with the following
- (void)setupVBOs {
glGenBuffers(1, &_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
glGenBuffers(1, &_indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
glGenBuffers(1, &_vertexBuffer2);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer2);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices2), Vertices2, GL_STATIC_DRAW);
glGenBuffers(1, &_indexBuffer2);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer2);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices2), Indices2, GL_STATIC_DRAW);
}
// 3) Add inside render:, right after call to glViewport
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
// 4) Add to bottom of render:, right before [_context presentRenderbuffer:...]
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer2);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer2);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _fishTexture);
glUniform1i(_textureUniform, 0);
glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix);
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 7));
glDrawElements(GL_TRIANGLE_STRIP, sizeof(Indices2)/sizeof(Indices2[0]), GL_UNSIGNED_BYTE, 0);
In the first section, we define a new set of vertices for the rectangle where we’ll draw the fish texture. Note we make it a little bit smaller than the front face, and we also make the z coordinate slightly taller so it will show up. Otherwise, it could be discarded by the depth test.
In the second section, we store the vertex/index buffers we create in the new instance variables rather than local variables. We also create a second vertex/index buffer with our new vertices/indexes for the fish rectangle.
In the third section, we bind the cube vertex/index buffer before drawing it, because we can no longer assume it’s already set.
In the fourth section, we bind the fish rectangle vertex/index buffers, load in the fish texture, and set up all the attributes. Note we draw the triangles with a new method – GL_TRIANGLE_STRIP.
After the first three vertices, GL_TRIANGLE_STRIP makes new triangles by combining the previous two vertices with the next vertex. This can be nice to use because it can reduce the index buffer size. I use it here mainly to show you how it works.
Compile and run, and it works (sort of):
It drew our fish image, but it didn’t nicely blend the fish image with the rest of the drawing going on. To enable this just add the following two lines to the top of render:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
The first line uses glBlendFunc to set the blending algorithm. It’s set to GL_ONE for the source (which means “take all of the source”) and GL_ONE_MINS_SRC_ALPHA for the destination (which means “take all of the destination except where the source is set”).
For more discussion on blending modes, check out this tutorial for more information and a pointer to a cool online tool.
The second line enables blending. And that’s it! Compile and run, and now you have a strange textured cube with a strange fish bone on it!
It’s a strange world, the full moon must be out tonight!
Where To Go From Here?
Here is the full sample project we created in the above tutorial.
At this point you’re starting to know some of the most important aspects of using OpenGL ES 2.0 – adding vertices, creating vertex buffer objects, creating shaders, texturing objects, and more!
There’s still a lot more to learn though, so at some point I hope to return to this tutorial series to add some more cool info.
In the meantime, if you want to learn more I recommend iPhone 3D Programming by Philip Rideout – I got started by reading this book!
If you have any questions, suggestions, or tips, please join the forum discussion below!