No 'slide' images have been detected!
How To Add Images To Your Slider
  1. View your project in edit mode by clicking the edit button.
  2. Import or drag and drop an image into your editor.
  3. Double click the image and in the popup menu change the filename to something that starts with 'slide'.
  4. Add as many images as you want and make sure to also change the filenames to something that starts with 'slide' (e.g. slide-2.jpg, slide-3.jpg).

Software, Hardware, Research – Results

OpenGL

OpenGL ES multi-texture example

This is the first in what I hope to be a couple of examples on using OpenGL on the iPhone. As we all know, there are examples around, but not that many. Jeff LaMarche is porting some of the NeHe tutorial examples, which is great.

I know that OpenGL ES 1.1 is what’s in use on the iPhone, but have been working my way through the OpenGL ES 2.0 Programming Guide anyway, trying to make things work.

To make a long story short, I started by wanting to do some water effects alá Koi Pond and ended up looking at multi-texturing. After some thrashing around, the solution is really straightforward. The main “mental” hurdle (in my case) was to remember that OpenGL implementations are state machines. You set the state and then execute the rendering.

What we’re going for in this example is what is shown on page 223 of the OpenGL ES 2.0 Programming Guide, namely one texture (brick wall) modulated by another (a light map). The effect is that of a light shining on a wall.



To make this simple, I’ve based this example on the PVRTextureLoader example from Apple, which you need to get and, if you are going to run the example on a device, configure for your registered device(s). We’ll stick with the brick texture that comes with it, but will need a light map for the modulation. For that you can download the OpenGL ES 2.0 Programming Guide sources and grab the image, or just use the image below.




The inspiration for the multi-texturing was taken from the Oolong engine Per-PixelLighting example (Oolong Engine2/Examples/Renderer/). All the code changes required are made in the EAGLView.h and EAGLView.m files.

The light map is a second texture, so we need to define an index for it to be used in the GLuint _textures[kNumTextures] array. Near the top of EAGLView.h, add the enum indicated below.

enum
{
kTexturePvrtcMipmap4,
kTexturePvrtcMipmap2,
kTexturePvrtc4,
kTexturePvrtc2,
kTextureMipmap,
kTexture,
kTextureLightMap,
kNumTextures
};



Add the lightmap.png file to the Resources group in the project.

Now in EAGLView.m, add a loadImageFile statement to the loadTextures method after the two existing statments;

	[self loadImageFile:@"Brick" ofType:@"png" mipmap:TRUE texture:kTextureMipmap];
[self loadImageFile:@"Brick" ofType:@"png" mipmap:FALSE texture:kTexture];
[
self loadImageFile:@"lightmap" ofType:@"png" mipmap:FALSE texture:kTextureLightMap];



The last change is to the drawView method. Instead of showing diffs, here’s the whole method;

- (void)drawView
{
const GLfloat squareVertices[] = {
-
1.0f, -1.0f,
1.0f, -1.0f,
-
1.0f, 1.0f,
1.0f, 1.0f
};
const GLfloat squareTexCoords[] = {
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0
};
GLenum err;

[EAGLContext setCurrentContext:_context];

glBindFramebufferOES(GL_FRAMEBUFFER_OES, _viewFramebuffer);
glViewport(
0, 0, _backingWidth, _backingHeight);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustumf(-
1.0f, 1.0f, -1.5, 1.5, 1.0f, 10.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glScalef(_scale, _scale,
1.0f);
glTranslatef(
0.0f, 0.0f, -2.0f);
glRotatef(_rotate,
1.0f, 0.0f, 0.0f);

glClearColor(
0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

glVertexPointer(
2, GL_FLOAT, 0, squareVertices);
glEnableClientState(GL_VERTEX_ARRAY);

// we'll be doing multitexture operations, so we need to specify texture
// coords for all (2 in this example) textures
glClientActiveTexture(GL_TEXTURE0);
// first texture
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(
2, GL_FLOAT, 0, squareTexCoords);
glClientActiveTexture(GL_TEXTURE1);
// second texture
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(
2, GL_FLOAT, 0, squareTexCoords);

// Enable 2D texturing for the first texture.
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);

// Enable 2D texturing for the second texture.
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);

// We are trying for the multi-textured lighting effect from the OpenGL
// ES 2.0 book, page 223, Figure 10-3. The relevant shader equation is
// basecolor * (lightcolor + 0.25), so we set the constant as a base colour
glColor4f(
0.25f, 0.25f, 0.25f, 0.25f);

// Set the light map has the current texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _textures[kTextureLightMap]);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _minTexParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _magTexParam);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, _anisotropyTexParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

// add the light map to the constant 0.25
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS);

// Now set the background map (bricks) and modulate (multiply)
glActiveTexture(GL_TEXTURE1);
// we'll keep this code here as _texSelection will never be our lightmap
glBindTexture(GL_TEXTURE_2D, _textures[_texSelection]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _minTexParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _magTexParam);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, _anisotropyTexParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

/* Set the texture environment mode for this texture to combine */
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

/* Set the method we're going to combine the two textures by. */
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);

/* Use the previous combine texture as source 0*/
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);

/* Use the current texture as source 1 */
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);

/*
Set what we will operate on, in this case we are going to use
just the texture colours.
*/

glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);

glDrawArrays(GL_TRIANGLE_STRIP,
0, 4);

glDisable(GL_TEXTURE_2D);

glBindRenderbufferOES(GL_RENDERBUFFER_OES, _viewRenderbuffer);
[_context presentRenderbuffer:GL_RENDERBUFFER_OES];

err = glGetError();
if (err != GL_NO_ERROR)
NSLog(
@"Error in frame. glError: 0x%04X", err);
}



I’ve added extra comments to help understand the changes. The only other thing worth noting is the glTexParameteri statements applied to the light map texture. Since the glBindTexture call for the light map is fixed, we could have moved these outside the drawView method and saved some cycles. However, I put them in as the germ of a “reader exercise” to add mipmap support for the light map. When you try the demo controls, you’ll see what I mean ;-)

The final result should look like the screenshot below.

After this, I’ll be playing with normals. I’ve managed to create a faux distortion effect by shifting texture vertices, so the next step is to look at lighting and surface effects...