OpenGL|ES tutorials - Follows NeHe track.

Ok guys, several people have been asking about OpenGL|ES development and have been wanting guides on how to get started. I've written a couple tutorials without discussing the iPhone SDK to help myself learn and to share with others. Please feel free to comment and I'll be adding to it as i complete them. The first post will be setting up a default starting point for further tutorials as well as getting the first triangle to draw on the screen. Please enjoy.

Drawing a Triangle
Ok, if you haven't already, please go look at the tutorial for Setting up an OpenGL Window. We will begin from there and get started into OpenGL programming.
Our first project will be a triangle. Go ahead and duplicate the OpenGLCore project. Rename it as you see fit.
Open the project and go to OpenGLCore.h
Next go to AppController.m and locate the setupView function. Here we will go typical OpenGL setup.
First we set up some constants to control how much of the screen we will see. Many of you are used to tutorials where you use gluPerspective. This was one duplicate function that was removed from OpenGL|ES however we can easily add it back in as our own function which will be presented at the end of the article.

Ok, so we have set up our view, told it how much we want to be able to see and centered ourselves on the origin. Next we want to tell OpenGL what color to keep our â€œbackgroundâ€, in this case a nice black.

Code:

// Clears the view with black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

So, much of the above is pretty typical of your standard OpenGL intro. The only thing vastly different up to this point is the lack of setting a depth buffer and using the glFrustum function instead of gluPerspective.

Now we are totally initialized and we are ready to do some real drawing. Go to the drawView function. This is where things take a different turn from most OpenGL tutorials. Firstly, there is no glBegin and glEnd in OpenGL|ES.

Lets begin with the first major difference. Borrowing from most tutorials, you are used to seeing:

Because our draw code gets called every time by the animation timer we need to clear the screen before we draw.

Code:

glClear(GL_COLOR_BUFFER_BIT);

When we last left off in setupView, we were on the model view, so we will reset our position so that other rotations don't compound our view and cause weird things to happen.

Code:

glLoadIdentity(); // Reset The Current Modelview Matrix

Next we will move ourselves so that the triangle doesn't show up in the center of the screen.

Code:

glTranslatef(0.0f,2.0f,-6.0f); // Move up 2 Units And Into The Screen 6.0

Now we are going to create a vertex point that contains our triangle's vertexes and enable the Vertex Array in OpenGL. We do this by telling glVertexPointer that we are giving it values in groups of 3, that they are floats, and that we want it to begin at the beginning of the array, and then tell it what array to use.

Lastly we will be drawing our array on the screen. We tell it that it is a triangle strip, that we want it to start at array index 0, and that we want it to use 3 vertexes from the array. We provided it with 3 sets of 3 vertexes, so that's what it will draw.

Code:

glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);

Save the AppController.m file, and build the application and you should see a nice white triangle appear just above the center of the screen.

Now I promised that I would show how to do gluPerspective. Its pretty simple.

Coloring our triangle
Last time we left off with a single blank triangle. Not much fun. Today we will add a little bit of code that will get us to a colored triangle. We will start with AppController.m from our last project.
Just after our creation of our vertexes, we will now create an array of colors that correspond to the points of the triangle.

Code:

const GLubyte triColors[] = {
255, 0, 0,
0,255,0,
0, 0, 255
};

Now, after we enable the VERTEX_ARRAY client state we want to add in 2 more lines of code to make use of our color scheme. Here we set our color pointer to tell it we have 3 unsigned bytes per color. Start at index 0, and give it the triColors. Then we need to enable the color array in our client.

Rotating
Last time we discussed the easy addition of color to our triangle. This time we will be covering rotation. Again, this will be a short tutorial as it is really simple.
Lets begin by adding a variable to our AppController.h file called rtri. This will hold our value for how much we are rotating our triangle by.

Code:

GLfloat rtri;

Because we are only using it in AppController.m we won't be adding a property or synthesizing.
Next open up AppController.m and we will add the meat to this. First we will want to initialize our rtri value so go into SetupValue and initialize it to 0.0f immediately after our glClearColor.

Code:

rtri = 0.0f;

Next we go to the DrawView. Immediately after the glLoadIdentity we will want to rotate our view by the rtri rotation value. We rotate around the y-axis by rtri degrees with the following call.

Code:

glRotatef(rtri,0.0f,1.0f,0.0f); // Rotate The Triangle On The Y axis ( NEW )

Once we have rotated our view, feel free to increment rtri by whatever makes you feel comfortable. I went ahead and added it after glDrawArrays.

Code:

rtri+=1.0f;
if(rtri > 360.0f)
{
rtri -= 360.0f;
}

So there we have it. We rotate our triangle by 1 degree every frame.
Here are SetupView and DrawView in their entirety as of this lesson.
SetupView:

glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity(); // Reset The Current Modelview Matrix
glTranslatef(0.0f,2.0f,-6.0f); // Move Left 1.5 Units And Into The Screen 6.0
glRotatef(rtri,0.0f,1.0f,0.0f); // Rotate The Triangle On The Y axis ( NEW )

Are these tutorials helping anyone out? I'm just wondering if i should continue to try and recreate the NeHe tutorials or if i should just drop it?

I'm half way through the next tutorial with a spinning pyramid. Currently I'm working on the spinning cube. I am trying to generate the easiest way to assign multiple vertexes the same color and then change the color associated with them for each face. This would give 8 vertexes and 6 colors instead of 6 arrays of 4 vertexes and 6 arrays of 4 colors.

Also, I am going to produce these as PDF files tonight. I'll post the link as soon as they are converted.

Are these tutorials helping anyone out? I'm just wondering if i should continue to try and recreate the NeHe tutorials or if i should just drop it?

I'm half way through the next tutorial with a spinning pyramid. Currently I'm working on the spinning cube. I am trying to generate the easiest way to assign multiple vertexes the same color and then change the color associated with them for each face. This would give 8 vertexes and 6 colors instead of 6 arrays of 4 vertexes and 6 arrays of 4 colors.

Also, I am going to produce these as PDF files tonight. I'll post the link as soon as they are converted.

Worked through Lessons 2 & 3, very well done!

There was one error though in your original code:

Code:

glVertexPointer(3, GL_FLOAT, 0, triVertices);

The original triangle array was called triVertexes. Changing both to one or the other gets rid of the error (which you did in your composite code at the bottom). I know that for newbs like myself this type of thing can be a real hang up.

Well, sorry it took a while. I was busy, hope you enjoy lesson 5. Also, please feel free to download the PDF versions of these lessons from my site. The links are in the initial post. Enjoy!

Drawing 3D models that rotate
Today we will look into the creation of 3D models for both the Pyramid and the Cube from the NeHe tutorials. This will take a divergent step away from what we are used to doing. We are going to create our 5 points for our triangle and create an index list along the same way that GLGravity did.

This array shows how we set up our pyramid along with a color guide to help us order our index list. Something to keep in mind is that the order of our vertexes in the index must correspond to the order of the colors in the color array. We also need a count of our indexes, this is the total indexes in triIndexes including the number at the front.

Later you will see that we change from glDrawArrays to glDrawElements. Because of this we also need to modify our triColor array. This array now corresponds to each color in our index list. Because we have 5 points, we now need 5 arrays. Our top point is red, points 1 and 3 are green, and points 2 and 4 are blue. So here is our new color array.

Thats all there is to it. Now we have a rotating pyramid with multicolors.
Now we are ready to draw our cube. But first we need to add the cube data.
Start with AppController.h and add a new variable rquad to the class. Again, we will not be setting a property or synthesizing.

Code:

GLfloat rquad;

Once we have that set we are finally going to set the depth buffer, otherwise we will be looking at a very weird cube.
Go into AppController.m and go to the setupView function. After the size variable but before we go into projection mode add the following.

Code:

glEnable(GL_DEPTH_TEST); // Enables Depth Testing

This allows OpenGL to do testing for which faces are physically in front of others. OpenGL has a glClearDepth function, but OpenGL|ES removed this function as well and just assumes you want to clear the entire buffer.
Also, because we added rquad, we need to initialize it in our setupView function. After the initialization of rtri, add rquad.

Code:

rquad = 0.0f;

Now that we have cleared our buffer, now we need to tell the clear function when to actually clear it so it can recalculate the face order. To do this we add a flag to glClear in the drawView function. Replace the old one with this new one:

Code:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

There, now we are clearing our depth buffer every frame.
Ok, everything is now in place we can focus on drawing our cube.
Here I need to introduce a few things that we normally aren't used to seeing as OpenGL usually goes. Because we are using GL_VERTEX_ARRAY and GL_COLOR_ARRAY to draw our triangle, we need to clean up when we are done by disabling these. Right after we finish our drawElement for loop, we need to disable these, in case we don't use them in further draw methods.

Its good to clean up your client states after you are done drawing (later you can comment these out and see how it affects the cube ~_*)
So, now you are wondering, how to I draw a cube. Well here goes. We begin by adding some new code after the state disabling.

Code:

const GLfloat cubeVertices[] = {
-1.0f, 1.0f, 1.0f, // 0, Top Left Front Of The Cube 4+-----+5
1.0f, 1.0f, 1.0f, // 1, Top Right Front Of The Cube /| /|
1.0f,-1.0f, 1.0f, // 2, Bottom Right Front Of The Cube / |7 / |
-1.0f,-1.0f, 1.0f, // 3, Bottom Left Front Of The Cube 0+-----+1 +6
-1.0f, 1.0f,-1.0f, // 4, Top Left Back Of The Cube | | /
1.0f, 1.0f,-1.0f, // 5, Top Right Back Of The Cube | | /
1.0f,-1.0f,-1.0f, // 6, Bottom Right Back Of The Cube 3+-----+2
-1.0f,-1.0f,-1.0f // 7, Bottom Left Back Of The Cube
};

// We need the number of faces we will be rendering, this is 6 faces, 5 values per face
int cubeIndexCount = 30;

Notice that we aren't doing anything with the color array, this is why we turned it off after our last drawing completed. Instead we will be using glColor4f as part of the for loop you will see next. Now the syntax might seem a little strange to newer programmers who have never seen a for loop work with more than 1 variable. Here goes.

The for loop initializes 2 variables, i and j. 'i' is used for the cube indexes like the triangles before, but j is used to control the cube colors. The middle of the for loop is the same, only 1 ending type. The last part is 2 increment phases, we need to increment 'i' by the number of indexes we have to advance, but j needs to be incremented by the number of colors we are using. Notice the comma to seperate the 2 statements in each section of the for loop. Neat huh?
Ok, like before, we are using a Vertex array, so we need to disable the client state after we finish drawing, do that now.

Code:

glDisableClientState(GL_VERTEX_ARRAY);

Lastly we added that new rotate variable for the cube so we will decrement it to make it spin a different direction.

Code:

rquad-=0.75f;
if( rquad < -360.0f)
{
rquad += 360.0f;
}

Thats all there is to it. Go ahead and compile and run the code and check out our nice new rotating cube.

I found that glColorPointer is the problem. The documentation says, that the size argument can be 3 or 4.
If I set it to 3 it doesn't work. 4 works great after you added a 4th value to the color array so that you've got 3 RGBA colors. If you want all colors full opaque, set it to 255. After that it works an your phone. At least on mine.