So you’re here to learn OpenGL ES 2.0. Great, it’s indeed a lot of fun! Many people may say that learning OpenGL is difficult, but all things in life are difficult before we know them, right? All you need is some patience. I know there are quite a few tutorials on this topic out there already, but I’m sure there is room for another. I remember being new to OpenGL myself, and I know how complex it can appear in the beginning. Have faith, though! It’s extremely rewarding once you’re starting to grasp it.
OpenGL ES 2.0 is a broad subject, so where do we start? I’d say we start from the very beginning.
In case you want to jump straight into the action, this project can be downloaded from GitHub.
1. Create an Xcode project
You already know how to create a new project, don’t you? Then go to your target and select “Build Phases”. Expand the “Link Binary with Libraries” list and add “OpenGLES.framework” and “QuartzCore.framework”. Your target should now look something like this:
Next, we make these frameworks available to all files by adding them to our prefix file.
So let’s create a view controller. I prefer a simple one, all below code will be written in the viewDidLoad method, so that’s really the only method we need here.
Finally, we need somewhere to display our OpenGL content. For that we need a custom subclass of a UIView, let’s name it GLView. It needs only one method for this tutorial, layerClass, and that should return the CAEAGLLayer class.
2. Create a context
Now, go to the viewDidLoad method of the view controller. All code from now on goes in that method. We start out by creating a context. The context is what keeps everything together. Without it, we can’t do anything, so we’d better create it. Luckily, it’s easy!
EAGLContext *context = [[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2] autorelease]; [EAGLContext setCurrentContext:context];
The parameter kEAGLRenderingAPIOpenGLES2 states that we are targeting version 2.0 of OpenGL ES. This is supported on iPhone 3GS and up and unless you really, really want to target older devices, I would advice you to forget about kEAGLRenderingAPIOpenGLES1. OpenGL ES 2.0 is the future. And a lot more fun!
3. Create a view
The view is of course also really important, since we need somewhere to draw. Since it’s a subclass of UIView, we can just create it like usual.
GLView *glView = [[[GLView alloc] initWithFrame:CGRectMake(0, 0, 320, 320)] autorelease]; [self.view addSubview:glView];
4. Create a renderbuffer
Unfortunately, OpenGL can’t draw directly to our GLView, but it can draw to something called a renderbuffer. That’s where the actual pixel data is stored. So what we do is that we draw to the renderbuffer, and then later we transfer our drawing to the glView. Doesn’t sound that complicated, right? Creating the renderbuffer is a three step process.
GLuint renderbuffer; glGenRenderbuffers(1, &renderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1. Create an unsigned integer.
2. Let OpenGL generate a renderbuffer for us.
3. Bind the renderbuffer to GL_RENDERBUFFER. Binding is like saying to OpenGL “Hey! From now on, whenever I say GL_RENDERBUFFER, I really mean the renderbuffer (unsigned integer) that I created earlier”. In other words, GL_RENDERBUFFER becomes an alias of ‘renderbuffer’.
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)glView.layer];
Finally, let OpenGL create storage for the pixels that we’re going to draw to. The context can help us with that. By passing the glView.layer, the context calculates how many pixels we need and allocates memory for them. The other thing we’re passing is GL_RENDERBUFFER, which now means ‘renderbuffer’ as we just described.
5. Create a framebuffer
You may be a bit confused now. Why the heck do we need a framebuffer when we already have a renderbuffer. Aren’t those the same thing? No, not really. The framebuffer contains the renderbuffer. It can also contain other things, like a depth buffer that stores depth values for each pixel, but we don’t need that now. In this example, the only thing the framebuffer contains is pixel color information. Creating the framebuffer is a three step process, very similar to creating the renderbuffer.
GLuint framebuffer; glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
1. Create an unsigned integer.
2. Let OpenGL generate a framebuffer for us.
3. Bind it. When we have bound it GL_FRAMEBUFFER now means ‘framebuffer’. The last thing we need to do is to connect the framebuffer and the renderbuffer. This is done by glFramebufferRenderbuffer. GL_COLOR_ATTACHMENT0 defines that the renderbuffer we’re passing holds color information (pixels), not depth information. Since the framebuffer also can contain a depth buffer, we need to be clear on this.
Now everything is set up for us to draw! The easiest way for us to output something to the renderbuffer is to define a clear color, and then clearing. This is easy.
glClearColor(1, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT);
1. glClearColor defined the color we want to with four parameters, RGBA. We like red so we’re setting a red color with full alpha, but you can modify that to the color of your choice. Colors are defined from 0 to 1 instead of 0 to 255.
2. glClear “draws” our red color to the renderbuffer.
7. Transfer the pixels to our view
We have now drawn some nice red pixels to the renderbuffer. All we need now is to transfer those pixels to our view. This is done by presentRenderbuffer. And remember, GL_RENDERBUFFER, points to our renderbuffer.
You should now see a red 320x320px square in your view controller. It may not be the most exciting thing you’ve ever seen, but it’s a great start!
8. That’s all, folks!
That wasn’t so hard, was it? The next tutorial is about shaders. Drawing a Square.