Friday, December 12, 2008

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.

No comments:

Post a Comment