Showing posts with label NeHe. Show all posts
Showing posts with label NeHe. Show all posts

Sunday, December 14, 2008

OpenGL Postings

These past three days have seen a flurry of postings. I thought I'd just do a quick table of contents to the various tutorials and postings I've done recently, in the order they should be read:

Just for Fun: OpenGL Icosahedron

Just to show that the complexity of the model doesn't necessarily increase the complexity of the code, here is an example iPhone project that shows how to display and rotate an icosahedron aka a twenty-sided platonic solid aka a virtual twenty-sided die. If you look at the project, you'll see that the code is virtually identical to the code for displaying and spinning the cube from the ported NeHe lesson 5, there's just more vertices.

Saturday, December 13, 2008

NeHe Tutorials, Lesson 6 Ported to iPhone (sort of)

I've ported NeHe Lesson 06. Sort of.

You see, OpenGL ES 1.1 doesn't support quads. NeHe selected a pyramid and a cube because they are easy shapes under OpenGL proper. However, the only polygon that OpenGL ES 1.1 knows about is the triangle. There are several variations on it - triangle strips, triangle fans, and triangles. As you saw when we ported Lesson 5, we had to split each quad into two triangles. That wasn't so bad when we were just drawing flat colors, but now that we're looking to map textures, it becomes a whole heck of a lot harder, because mapping a square texture to a triangle is, quite frankly, a pain in the ass.

Here is the link for the ported Lesson 6 project.

I only mapped two of the sides - front and back, the other aren't done and won't display right. It took me the better part of an hour before I finally gave up mapping all six sides. I hate releasing this unfinished, but I just can't afford the time right now to manually map the textures to all the triangle that make up the cube. Fortunately, there's rarely a need to manually map texture coordinates any more. Most 3D programs will export the texture coordinates for you, so all we really need to take from Lesson 06 is how to load the textures into memory and how to make them active. You should read the NeHe tutorial so you understand how texture mapping works, but it doesn't make sense to spend a lot of time on the process. In a future posting, we'll see how to take a 3D object created in Blender and import the texture coordinates along with the vertex data. In practice, it would be rare for you to manually map textures to individual polygons like this.

In Lesson 06, we have our first instance variable in the controller class. Because I needed to access the textures array from multiple methods, I couldn't just make another static variable. So, we now have an array of integers, and these integers will each refer to a texture loaded into OpenGL.
GLuint  texture[1];      // Storage For One Texture ( NEW ) 
In our setupView method, we prepare our texture just like is done in the NeHe Lesson 06
 glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
We have to vary a little from the plan to actually load the texture from file. Instead of a .bmp file, we're going to load that compressed texture file I had you create in this blog posting. If you didn't do that, go do it now. You'll want to drag the .pvr4 file into the /Resources group of your project.

Once you've got the project in your texture, we can load it in our setupView method:
 NSString *path = [[NSBundle mainBundle] pathForResource:@"NeHe" ofType:@"pvr4"];
NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
// Instead of glTexImage2D, we have to use glCompressedTexImage2D
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 256.0, 256.0, 0, (256.0 * 256.0) / 2, [texData bytes]);
[texData release];
A couple of things to notice - first, because our texture image is compressed, we had to use glCompressedTexImage2D() rather than glTexImage2D() as was done in the NeHe tutorial. The other thing is that we release the image data after we feed it to OpenGL - it creates its own copy of the texture, so we have to release to avoid leaking memory.

After that, the rest of the setup is pretty much the same as in the original tutorial:
 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
// Enable blending
glEnable(GL_BLEND);
Where things get really different is in the drawView method. Some of you probably saw this coming, but we can't use glTexCoord2f() because we can't use glBegin() and glEnd() on the iPhone.

The solution is the same as before - we stuff all those texture coordinates into an array called (wait for it) a texture coordinate array. Texture coordinates in the array are just like the values you would pass to glTexCoord2f() - values between 0.0 and 1.0. Refer to the original tutorial for a description of how that mapping works. It's a little hard to grok. If you don't fully get it, don't worry - before long, we're going to write an object-loader that will handle that for us, and then we'll never have to remember how it works again. Here is the texture coordinate array I created for this lesson, but remember that only the first 16 values (corresponding to the first four polygons that make up the front and back of the cube) are actually valid and mapped.
 const GLfloat cubeTextureCoords[] = {
1.0, 1.0, // Front
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,

0.0, 0.0, // Back
0.0, 1.0,
1.0, 1.0,
1.0, 0.0,

0.0, 0.0, // Top
1.0, 1.0,
1.0, 1.0,
1.0, 1.0,

0.0, 0.0, // Bottom
0.0, 1.0,
1.0, 1.0,
1.0, 1.0,

0.0, 0.0, // Left
1.0, 1.0,
1.0, 1.0,
1.0, 1.0,

0.0, 0.0, // Right
1.0, 1.0,
1.0, 1.0,
1.0, 1.0,
};
Before we make any calls to glDrawElements(), we have to tell OpenGL about the texture coordinates, and we do that using a function called glTexCoordPointer(), like so:
glTexCoordPointer(2, GL_FLOAT, 0, cubeTextureCoords);
The first argument to this function says that we have two mapping values for each element to be drawn. The second argument tells OpenGL that the texture array contains floating point values, the third is that parameter I keep telling you to ignore and just provide 0 for, and the last is the pointer to the actual texture coordinate array.

Before you can call glDrawElements, you have to enable texture coordinate arrays, by making this call:
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
Like the other enable functions, you can call this once during setup, or you can enable and disable it during application execution. Because only one of our two objects is getting mapped, we enable it before we draw the cube, and disable it afterwards, which will keep the pyramid from getting mapped as well.

In order for texture mapping to work, we must also call
glEnable(GL_TEXTURE_2D);
This only has to be called once in setup, or you can also enable and disable it during application execution, though typically it's just turned on at the beginning.

That's pretty much it. When we call glDrawElements(), it will automatically fetch the coordinate data and map the last texture that was bound. We only have one, which we bound during setup by calling glGenTextures() and glBindTexture().

If we had a lot of textures (which you will in most situations), you would typically load them all in during setup, and then activate different ones by making subsequent calls to glBindTexture(), which does double duty, both creating, and activating existing textures. The last one that was passed to glBindTextures() is the one that will get mapped when you draw.

This was a tough lesson to port. They opted to map the cube because it was easy - square sides, square textures, very simple mapping. We're not that lucky on the iPhone, so I hope you'll forgive the fact that this one is incomplete. In return, I promise to show you how to load objects with texture maps. Probably not for a week or two, though.

Lesson 5 Code Fixed

I've got the Lesson 5 code corrected. I think I'll leave Lesson 05 as is, and make the change I'm thinking of in 06, since it will make texture mapping easier, but doesn't really add much to the Lesson 5 discussion.

OpenGL Warning!

If you've downloaded my OpenGL template, or any of the ported NeHe lessons (except Lesson 02, which isn't affected), please re-download them. One of the files was linked incorrectly in the project, causing edits to go to the template, not to the file. I apologize for the inconvenience - it's been driving me crazy for a day or two, but it took me that long to figure out why things were behaving so strangely.

I've updated the template and lessons 3 and 4. I'm still working on 5 and also looking at changing the approach - I'll try to get this all straightened out by the end of the weekend.

Preparations for Porting NeHe Lesson 06

The next lesson has to do with texture mapping. The NeHe lessons load a bitmap image and then maps that image to quads. Because the iPhone has less memory than desktop computers, we're going to use a different image type In order to conserve memory. This is a special image format used specifically for texture mapping called PVRTC.

Before we can move ahead with this, there are two things you need. The first, is a source image that will be mapped onto. You can pull the one from the NeHe Lesson 6 C++ project, or you can use your own image. If you use your own image, it must be sized so that both directions are equal and are a power of 2, which means the image should be 2x2, 4x4, 8x8, 16x16, 32x32, 64x64, 128x128, 256x256, 512x512, or 1024x1024 pixels (which is the largest size texture supported on the iPhone).

There's a command-line tool that will create PVRTC images for you from source PNG files. That tool is located at the following location:
/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/texturetool

To make life easier, you might want to add /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ to your path variable. I use tcsh as my shell, so adding that to my path was just a matter of adding /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ to my .cshrc file (~/.cshrc). Here's what that line in my .cshrc file looks like:
set path= ( /usr/X11/bin /opt/local /opt/local/bin ~/bin /usr/local/bin /usr/bin /bin  /usr/sbin /sbin /Users/jeff/bin /Developer/Tools /opt/local/bin /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ )
If you're a bash person, then you would do this in your bash profile, located at ~/.bash_profile, and the syntax is a touch different:
export PATH="/opt/local:/opt/local/bin/:Users/jeff/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/Users/jeff/bin:/Developer/Tools:/opt/local/bin:/usr/local/pgsql/bin:/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/"
What that will do is allow you to use texturetool from the command line without having to type the full path, just as if texturetool were sitting in your /bin directory.

Now, in Terminal, navigate to where your source PNG image file is using the cd command, then generate your PVRTC file with the texture tool, like so (in my case, the source file is NeHe.png:
texturetool -e PVRTC -o NeHe.pvr4 NeHe.png
Running this will generate a file with a .pvr4 extension. You will not be able to view this image file in Preview.app, or the vast majority of other image editing or viewing programs (so make sure this conversion is the last step), but trust me, it's an image file. Save this file for the next lesson, lesson 06.

It may take a few days before I get Lesson 06 ported, though. The fact that our cube is made up of triangles, rather than quads is going to mean some trial and error with the texture coordinates.

Friday, December 12, 2008

NeHe Tutorials, Lesson 5 Ported to iPhone

Into the third dimension!



I've now ported NeHe OpenGL Lesson 5. This one's not exactly straightforward - my description of the difference will probably be longer than the original lesson 5. That's partially because of functionality that's not available on the iPhone, and partially because the iPhone is relatively resource-constrained device, so I implemented some optimizations that weren't strictly necessary, but which you should really use on the iPhone.

Here is the link for the ported Lesson 5 project.

Where do we even begin this time?

Defining & Drawing the Pyramid



First, let's talk about a basic fact of life. When we've been defining our vertices, we've been doing it using floating point numbers stored in GLfloat variables. Now, generally speaking, floating point numbers are slower than integers. Of course, with GPUs and FPUs it's not always that straightforward, but it's generally a good rule of thumb when programming, including programming for the iPhone.

We don't want to sacrifice precision, however. We want to define our vertices with floating point numbers, and then refer to them using integers. Take a look at this spiffy picture I drew (I know, can you believe I don't draw for a living?):

These are the vertices for the pyramid shape. Notice that we have a total of five vertices that make up this three-dimensional shape. However, we can't just feed those five vertices into OpenGL and expect it to know what to do with them. It doesn't work that way. We have to be a little more specific than that. We have to identify each of the faces or polygons that make up the shape.

For the pyramid, we're not going to draw the bottom, so let's not worry about that, but besides the bottom, a pyramid is made up of four triangular shapes, and we need to draw all four of them. So that's four triangles with three vertices each, for a total of twelve vertices that we need to feed to OpenGL to draw this pyramid. But, look at the illustration again: We only have five vertices total in the shape. That means, we're going to feed the same vertices into OpenGL multiple times, because the polygons or faces share vertices.

We could create a vertex array with all twelve points, and that would work fine. But it's inefficient, because we're making OpenGL toss around the same five vertices (made up of three floating point values each) multiple times. OpenGL is not going to look at the values and determine if they're the same - it's just going to use them to draw, and in the process, you're going to make it shuttle around and do calculations on a lot more floating point values than it needs to.

Instead of doing that, we're going to define a vertex array, just like before, and it's only going to have five values in it - one for each unique point in space that make up the polygon.
static const GLfloat pyramidVertices[] = { 
0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f
};
Then, we're going to feed that vertex array to OpenGL, again just like we did before. But, we're not going to use glDrawArrays() to tell it to draw the vertex array, as we've done before. Instead, we're going to define another array, but this one is not going to contain vertices, it's going to contain integer values holding index values that point into the vertex array that contains the five vertices. So, to refer to the top of the pyramid, instead of referring to it as {0.0f, 1.0f, 0.0f}, we are going to refer to it by that vertex's index within the vertex array.

This is a little confusing, however, because we're not going to refer to them using the index in the actual array, but rather we're going to pretend that each vertex in the vertex array is a distinct object, with its own index value, like so:
static const GLfloat pyramidVertices[] = { 
0.0f, 1.0f, 0.0f, // These three values are one vertex with index 0
-1.0f, -1.0f, 1.0f, // These three values are one vertex with index 1
1.0f, -1.0f, 1.0f, // These three values are one vertex with index 2
1.0f, -1.0f, -1.0f, // These three values are one vertex with index 3
-1.0f, -1.0f, -1.0f // These three values are one vertex with index 4
};
Or, to use our fancy illustration again, this is how we're going to think of these indices:

So, we'll create another array that actually defines each face of the pyramid (remember, we're not drawing the bottom). That array, can be made up of any of the OpenGL integer data types. I've chosen GLubyte because it's the smallest and I have less than 256 vertices in my array, so it will work just fine. Here is what that array looks like
static const GLubyte pyramidVertexFaces[] = { 
0, 1, 2, // Defines Front Face
0, 3, 2, // Defines Right Face
0, 3, 4, // Defines Back Face
0, 1, 4 // Defines Left Face
};
Pretty simple, right? You can see that we still have values repeated, but at least they're nice, small one-byte integer values that can be shuffled around quickly. Doing this also allows OpenGL to cache calculations that correspond to a specific vertex so that they don't have to be done multiple times.

For the pyramid, we're still using that color array to give us that fancy blended gradation, but we're going to optimize it. Colors can be defined using floating point value between 0.0 and 1.0. They can also be defined as an integer between 0 and 255. Because all of our colors can adequately be defined using one byte per color channel, this will be a tiny bit faster in OpenGL, and tiny bits faster can add up when you're programming on a mobile device like the iPhone. Here's the new version of the color array.
static const GLubyte triVertexColors[] = { 
255, 0, 0, 255,
0, 255, 0, 255,
0, 0, 255, 255,
0, 255, 0, 255,
0, 0, 255, 255
};
Why do we have more values this time, you might wonder? Well, we it's because we define a color for each element in the vertex array. When we refer to the vertex by its index value, OpenGL goes and grabs the matching color from this array, There are a total of five vertices, and we're assigning a color to each one. You want to the same number of colors in your color array that you have vertices in your vertex array.

Oh, here's another thing - in OpenGL, you can define colors with three values in a color array. Not, however, in OpenGL ES. You must use four values to define a color - you must provide the alpha value, even if it's always 1.0 or 255.

We also define a constant that identifies the number of vertices we're going to draw - this is the number of polygons or faces multiplied by the number of vertices in each polygon.

static const GLubyte triNumberOfIndices = 12
Now that we've got our vertex array, color array, and faces defined, we can draw. We're not going to use glDrawArrays(), we're going to use a new function called glDrawElements(). We will call this function once per polygon, which means we're not just feeding an array in and letting OpenGL do the rest, we're going to loop through our polygons and feed them, one at a time, to glDrawElements(). It's not as hard as it sounds, however. Here's how we do it:
 glVertexPointer(3, GL_FLOAT, 0, pyramidVertices); 
glColorPointer(4, GL_UNSIGNED_BYTE, 0, triVertexColors);
for(int i = 0; i < triNumberOfIndices; i += 3)
{
glDrawElements(GL_TRIANGLE_FAN, 3, GL_UNSIGNED_BYTE, &pyramidVertexFaces[i]);
}
Notice that we've changed our call to glColorPointer() to use GL_UNSIGNED_BYTE because we changed our color array to use GLubyte. Then we loop through our array of polygons, incrementing by three so that we only hit each triangle once, and we feed that triangle into the glDrawElements() call.

It's a little confusing (and it's not that easy to explain either), but it's really rather straight forward once you've wrapped your head around the basic idea. The glDrawElements() function knows that the values you're feeding are indices that point into the vertex array you provided to it earlier, and it draws a polygon based on the vertex indices that you feed it.

Our pyramid is made up of triangles (except the bottom, which we didn't draw), but what about the cube?

Defining and Drawing the Cube


Well, OpenGL ES doesn't support GL_QUAD. You can't draw squares in OpenGL ES. You can draw points, lines, line loops, triangles, and triangle fans, but not quads. So, how do we draw the cube, which is made up of six quadrangles?

In theory, it's easy enough. We just subdivide each square into two triangles. Draw a line from one corner to the opposite corner of any rectangle, and you've got two triangles. In fact, any polygon can be subdivided into triangles. So, instead of drawing six rectangles, we just have to draw twelve triangles. But, the theory is exactly the same as with the pyramid. We define all the vertices that make up the cube (eight of them) and then create a vertex array out of them. We then define, using those special, the twelve triangles that make up the cube. Here's what those data structures look like:
 static const GLfloat cubeVertices[] = { 
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f,-1.0f, 1.0f,
-1.0f,-1.0f, 1.0f,
-1.0f, 1.0f,-1.0f,
1.0f, 1.0f,-1.0f,
1.0f,-1.0f,-1.0f,
-1.0f,-1.0f,-1.0f
};
static const GLubyte cubeNumberOfIndices = 36;

const GLubyte cubeVertexFaces[] = {
0, 1, 5, // Half of top face
0, 5, 4, // Other half of top face

4, 6, 5, // Half of front face
4, 6, 7, // Other half of front face

0, 1, 2, // Half of back face
0, 3, 2, // Other half of back face

1, 2, 5, // Half of right face
2, 5, 6, // Other half of right face

0, 3, 4, // Half of left face
7, 4, 3, // Other half of left face

3, 6, 2, // Half of bottom face
6, 7, 3, // Other half of bottom face

};
Feeding them to OpenGL is exactly the same. One difference is that we're not creating gradations of color this time, but rather are drawing each side of the cube in a different color, so we define a color array, but we don't feed it to OpenGL as a color array, we use it in our loop to manually set the color. Here's the color array:
 const GLubyte cubeFaceColors[] = { 
0, 255, 0, 255,
255, 125, 0, 255,
255, 0, 0, 255,
255, 255, 0, 255,
0, 0, 255, 255,
255, 0, 255, 255
};
The only gotcha here is that we have six colors, for six sides, but we're drawing twelve triangles, so we have to remember to only increment the color index every other triangle. Here's the draw loop for the cube:
 glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
int colorIndex = 0;
for(int i = 0; i < cubeNumberOfIndices; i += 3)
{
glColor4ub(cubeFaceColors[colorIndex], cubeFaceColors[colorIndex+1], cubeFaceColors[colorIndex+2], cubeFaceColors[colorIndex+3]);
int face = (i / 3.0);
if (face%2 != 0.0)
colorIndex+=4;

glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, &cubeVertexFaces[i]);
}

Okay, I know this was a heavy posting. This posting is probably twice as long as the original NeHe lesson. That's the price to be paid for 3D on a mobile device like the iPhone. You can't rely on OpenGL ES to do for you all the things that OpenGL proper does for you. But, once you start to get the hang of it, it's really not that bad to work with.

I think I'm going to go off script after this posting. I'm not sure there's much value in going lesson-by-lesson through the next batch of NeHe tutorials... not until some of the more advanced topics. I do want to cover a couple of things, however - normals, and texturing, for example. So I think I'll take a break from NeHe for the next couple of postings and talk about what normals are and how they help in 3D drawing, and also show how to map textures to polygons. Then, maybe, I'll show how to load a 3D object created in a program like Maya or Blender, at least, once I've figured out how to do it myself.

NeHe Tutorials, Lesson 4 Ported to iPhone

NeHe Lesson 4 was very easy to port. The newly introduced functionality all pretty much works as is.

Here is the link for the ported Lesson 4 project.

The only noteworthy difference (other than items mentioned in the previous blog postings) is the fact that I declared rtri and rquad as static variables within the drawView method rather than declare global variables. This was just to keep all the relevant code for the project together, and doesn't have any impact on the program's functinoality.

One change I might recommend would be to tie the increases to rtri and rquad to the amount of time elapsed. It seems to run pretty smoothly, but if you wanted it perfectly smooth, that would be the way to accomplish it. To do this, replace this code:
 rtri+=0.2f;      // Increase The Rotation Variable For The Triangle ( NEW )
rquad-=0.15f; // Decrease The Rotation Variable For The Quad ( NEW )
with something like this code, which rotates at a steady 20° per second for the triangle, and 15° per second for the square.
 static NSTimeInterval lastDrawTime;
if (lastDrawTime)
{
NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime;
rtri+=20.0 * timeSinceLastDraw;
rquad-=15.0 * timeSinceLastDraw;
}
lastDrawTime = [NSDate timeIntervalSinceReferenceDate];
Another thing you can do - it won't change the functionality, but will help during debugging - is to keep the rotation within a 360° scope, which can be done simply by adding a couple of lines of code:
 static NSTimeInterval lastDrawTime;
if (lastDrawTime)
{
NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime;
rtri+=20.0 * timeSinceLastDraw;
if (rtri > 360.0)
rtri -= 360.0;

rquad-=15.0 * timeSinceLastDraw;
if (rquad < 0.0)
rquad += 360.0;
}
lastDrawTime = [NSDate timeIntervalSinceReferenceDate];

NeHe Tutorials, Lesson 3 Ported to iPhone

Here is the iPhone version of NeHe Lesson 3.

Here is the ported code for lesson 3.

Again, most of what's there is valid, and you should read the original lesson - I'm not going to cover everything - but there are some bits of functionality in Lesson 03 that are not available on the iPhone. <

First of all, glColor3f() does not exist in OpenGL ES - you have to use glColor4f() instead. The only difference is that you must pass in not only a value for red, green, and blue, but also for alpha. You could always define your own glColor3f() like so:

#define glColor3f(r,g,b) glColor4f(r,g,b,1.0)

Though it's probably easier if you just get used to using glColor4f().

The other bit of missing functionality is a little more complex, and it requires us to jump ahead of the tutorial once again. In the tutorial, when drawing the triangle, they change the color at each vertex. Between the calls to glBegin() and glEnd(), they set a color, define a vertex, set a new color, define another vertext, etc. OpenGL then interpolates the inbetween values to give a nice gradient effect. However, because we have to define our vertices using a vertex array in OpenGL ES, there's no way to change colors between the vertices the way they do in this lesson.

All is not lost, however. There is very little in OpenGL that can't be done in OpenGL ES, and in this case, we can achieve the same functionality by using a color array, which is just an array of values like the vertex array from last lesson. In the lesson, they set the color to red before defining the first vertex, then to green before the second, then to blue for the third vertex. We can achieve the same thing by defining another array of GLFloat values, like so:

const GLfloat triVertexColors[] = {
1.0f, 0.0f, 0.0f, 1.0f, // Red
0.0f, 1.0f, 0.0f, 1.0f, // Green
0.0f, 0.0f, 1.0f, 1.0f // Blue
};

In this case, we're defining each color with 4 values (red, green, blue, alpha), and our array has one color to correspond to each of the vertices in our vertex array, meaning an array of twelve items (4 floats per color times three vertices). When OpenGL goes to draw, it will use the first four values from this array to set the color for the first vertex in the vertex array, the second four values to set the color for the second vertex in our array, and the last four values to set the color for the last vertex.

In order to use a color array, we have to enable it, just like we did with vertex arrays. Again, this can be done once, in setup, or can be enabled and disabled as needed at runtime. Generally, you will enable it before you use it, and then disable it afterwords, otherwise future calls to glColor4f() will be ignored, which may not be what you want. Here is the call to enable color arrays:
glEnableClientState (GL_COLOR_ARRAY);
and to disable
glDisableClientState (GL_COLOR_ARRAY);

Then, before we call glDrawArrays(), we have to tell OpenGL about our color array. We do that like so:
glColorPointer (4, GL_FLOAT, 0, triVertexColors);
The call is very similar to our glVertexPointer() from Lesson 02 (which is also used in this project, of course). The first argument tells OpenGL that each color is made up of four values. The second argument tells OpenGL that those values are GLfloats. The third value is that one you can forget about for now, and the last one is the pointer to the color array.

That's all there is to it - run it now, and you'll get your pretty triangle, just like in the NeHe lesson:

NeHe Tutorials, Lesson 2 Ported to iPhone

The first substantive OpenGL lesson over at the great NeHe Productions website is Lesson 02

I have ported this lesson's code to the iPhone. Here is the source code.

You should read the lesson, but you should also note that the drawing technique used in the early NeHe lessons (using glBegin(); and glEnd();) is not supported on OpenGL ES 1.1. As a result, the project linked above uses something called vertex arrays, which aren't covered until much later in the NeHe lessons. Here is the basic difference in the drawing code:

In the NeHe tutorial, they do this:
glBegin(GL_TRIANGLES);     // Drawing Using Triangles
glVertex3f( 0.0f, 1.0f, 0.0f); // Top
glVertex3f(-1.0f,-1.0f, 0.0f); // Bottom Left
glVertex3f( 1.0f,-1.0f, 0.0f); // Bottom Right
glEnd(); // Finished Drawing The Triangle

The call to glBegin() tells OpenGL that the code that follows is going to define points (or vertices) in virtual space. Then the three calls to glVertex3f define the vertices using cartesian coordinates (x, y, z). Because GL_TRIANGLES was specified in glBegin(). OpenGL will draw and fill the triangle defined by the three vertices specified before the call to glEnd()

On the iPhone, instead of doing that, we have to place the same three vertices into an array (not an NSArray - a good old-fashioned C array). This array, oddly enough, is called a "vertex array". Here's what a vertex array containing the three vertices in the code above looks like:

const GLfloat triVertices[] = { 
0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f
};
Notice that we have nine elements in the array - that's three vertices with three values each (x,y,z). Vertices can be defined by either 2 or 3 points depending on whether you are doing three-dimensional or two-dimensional drawing. Although we are drawing flat, two-dimensional shapes here, we are actually drawing them in three-dimensional space, so the first value in our array is an x location, the second is a y location, and the third is a z location. Then we move on to the next vertex, with another x, then y, then z, etc.

In order to use vertex arrays, we have to enable that feature in OpenGL, like so:
glEnableClientState(GL_VERTEX_ARRAY);
The call to glEnableClientState can be either in the setup code, or in the drawing code, depending on your needs, and states can be enabled and disabled during the life of the application, so if you only use vertex arrays in one part, you could enable it, do your vertex array drawing, then disable it by calling glDisableClientState(GL_VERTEX_ARRAY);.

After defining the vertex array and turning vertex arrays on, we have to tell OpenGL ES to draw the vertex array. We do that like so:
glVertexPointer(3, GL_FLOAT, 0, triVertices);
This function tells it that the triVertices array is a vertex array, and that each vertex has three data elements (x, y, z). If we were doing two-dimensional drawing and using vertices with two values, we would have passed 2 instead of 3 for the first argument. The second argument GL_FLOAT tells OpenGL ES that our array is an array of floating point (GLfloat) values. The third parameter isn't used here, so we pass 0. This parameter can be used to "skip" values in a vertex array while drawing - don't worry about this, it's not something you're likely to use until you know more about OpenGL than I do. The final argument is a pointer to the array we're telling OpenGL about.

Now that OpenGL ES knows about the vertex array, we can tell it to draw the vertices in that array. We do that like so:
glDrawArrays(GL_TRIANGLES, 0, 3);

In this function call, the first argument mirrors the one used in the glBegin(); call in the original tutorial - it tells OpenGL which to use from several ways it knows how to draw. Some of the options include drawing points, lines, triangles, and several more advanced options.

And that's it - everything else from the tutorial should be the same - glIdentity(), glTranslate() etc, all function exactly the same, we just had to fast-forward a little to vertex arrays because of the limitations of OpenGL ES.