A quick look at GLKit

Those who know or follow me should know by now I focus a lot on Microsoft tech. Not really that I only love their tech and go tell the others to fuck themselves, but I’ve had the opportunity to go into details professionally, and not much on concurrent stuff. I also specialize in 3d, that’s why you can read some stuff about that on my blog, and I’ve done a bit of MS 3d tech : XNA, DirectX/SharpDx, HLSL, Babylon.js (it’s done by my MS mentors <3 ), etc…

Anyways, once in a while, I get to do other stuff, like Polycode, or even Löve2D when I feel I need a laugh. More recently I’ve been hired on a mission involving iOS and a whole bunch of 3d stuff. I can’t really describe it, but I get to do realtime opengl on an ipad as a daily basis. That said, you might like to know I’m a total ass concerning opengl and iOS in general. I’ve got a 1st gen ipad that’s so slow we mostly use it for Spotify OH SORRY, YOU NEED iOS6 FOR THAT (ever since my Nexus 7 died out for no reason and said app shows no interest whatsoever in Windows RT (YEAH I’VE GOT A WRT TABLET, DEAL WITH IT)), and a 2009 Macbook pro that’s got a broken screen, no battery, and no charger. Soooo for the iOS part, I’m totally lacking the experience. Regarding OpenGL, I can’t say I’ve done much. Back in the days (that would be around 2006-2008) I used to fiddle with C templates in Code::blocks without having an idea of what I was actually doing. Yet, I felt like I could overcome this : I’ve seen the fires of DirectX, and have returned to say “lolnope”. So yeah, I took the mission.

Anyways, a week or so later, I can say I’m pretty surprised : GLKit is really good! Basically, GLKit is a set of helpers for OpenGL ES 2 on iOS. This includes blazing fast vector and matrix math, full integration with UIKit (iOS GUI framework), predefined constants for operations like vertex layout and base shaders that work like a charm! Of course, you still have low level gl stuff to handle, like buffer upload and manual VAO management (even though the concept of VAOs has got to be the best thing in the world), but you can setup a simple OpenGL program in not much lines of code. So yeah, GLKit gets my thumbs up. Only sad thing is that it’s exclusive to iOS, and I’d be doing a lot more of OpenGL if you could use it on other platforms. I suppose there are equivalents out there, but as always, finding time for something new is kind of a luxury nowadays.

Anyways, to show you what I mean by simplicity, let’s code a fairly simple program that outputs a quad with uniform color. If you’re an opengl/iOS expert, please insult me on twitter if I’m doing something wrong. First things first, everything included with Apple’s GLKit is prefixed with GLK, and everything that belongs to opengl directly is prefixed by gl.

We’ll start by making a new xcode Cocoa touch project, with a single view template, because the point is to show you how few lines are enough to get you doing opengl. Since I’m working exclusively for ipad, my project is not universal, so that’s up to you. Once we’ve created the project, we’ll go into the storyboard(s) and modify the base view class to GLKView. This ensures GLKit encapsulates the view to make it available as a canvas to draw on. Next, we’ll go into the ViewController.h to add a call to GLKit headers and define our controller as a GLKViewController. Your ViewController should look like this :

#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>

ViewController : GLKViewController

That’s all we’ll do in the header file, let’s head over to the ViewController.m ! We’ll add the most important stuff : the context, of type EAGLContext. This is the object that will bind OpenGL to our view and tell OpenGL to draw on it. So in your ViewDidLoad, you will initialize this context and attach it to the view :

ViewController
{
    EAGLContext* context;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; // context creation
    // View setup
    GLKView* view = (GLKView*)self.view;
    view.context = context;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    [EAGLContext setCurrentContext:context];
}

You’ll notice the drawableDepthFormat in these lines. To make it simple, this is the depth format OpenGL will use for the render target, aka our view. If that’s not clear, Apple has a lot more to say about this. Now, let’s draw something! We’ll start by clear the screen with a horrible magenta color to ruin your eyes. As a reminder, the principle of clearing is to make sure you draw/render your calls on a clear frame. If you don’t do it, you’ll be drawing over the previous frame. And unless for some unknown reason this is what you are looking for, you want to clear your frame before drawing anything. It’s like an automatic background color. The color can be anything in the RGB + Alpha domain (limited by float precision in general), so you can get creative. So, we’ll start by overriding a method GLKViewController implements : -(void)glkView:(GLKView *)view drawInRect:(CGRect)rect. This convenient method is automatically called at regular intervals to draw a new frame. It’s 30 frames per second by default, and you can set this higher or lower with self.preferredFramesPerSecond in your controller. Aim for 60 FPS, your customers’ eyes will thank you. In this method, we’ll set the clear color and clear our main buffers : color and depth.

-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

The glClear method takes flags with specific masks to tell the GPU to clear specific buffers. Fortunately OpenGL is provided with a whole set of defines so that you don’t have to remember these masks. You can launch your app, and you should by now be screaming “DEAR LORD MY EYES”. Don’t thank me, it’s normal.

Okay, so that was fun, what more can we do? Let’s draw something! A quad! With indices (elements in opengl, don’t ask) to make it funnier! So like I said, GLKit provides a ton of math helpers, and we’re going to take advantage of 3d vectors to define our vertices in space. We’ll also define a bunch of indices to order our draw calls and send all of this to our GPU. Let’s add all of this inside the ViewDidLoad method, right after setting our context. Here’s the code as I explain it through the comments and bellow (scroll for more) :

    GLuint vertexArray; // our VAO
    GLuint vertexBuffer; // our VBO for vertices
    GLuint indexBuffer; // our VBO for indices

    GLKVector3 vertices[4];
    vertices[0] = GLKVector3Make(-0.5f, -0.5f, 0); // create a new GLKVector3 structure
    vertices[1] = GLKVector3Make(0.5f, -0.5f, 0);
    vertices[2] = GLKVector3Make(0.5f, 0.5f, 0);
    vertices[3] = GLKVector3Make(-0.5f, 0.5f, 0);
   
    GLuint indices[6];
    indices[0] = 0;
    indices[1] = 1;
    indices[2] = 2;
    indices[3] = 0;
    indices[4] = 2;
    indices[5] = 3;
   
    glGenVertexArraysOES(1, &vertexArray); // create the VAO
    glBindVertexArrayOES(vertexArray); // tell opengl we're using it
   
    // create the vertices VBO
    glGenBuffers(1, &vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); // tell opengl we're using it
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // upload the date to the GPU
   
    // same story for the indices VBO
    glGenBuffers(1, &indexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
   
    // tell opengl to activate position on shaders...
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    // and use this part of our vertex data as input
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLKVector3), 0);
    // close our VAO because we're done here
    glBindVertexArrayOES(0);

That’s a lot of code, but it’s pretty straightforward. The first 3 variables are identifiers to GPU buffers and the VAO, don’t worry about them now. First we create 2 arrays to store our vertex and index data. For vertices, we’re using GLKit’s implementation of a 3d vector, GLKVector3. It’s a C struct, so init is old school style with C functions. Indices use a base type of GLuint which is an alias for a platform unsigned integer. It’s easier to read and indicates this part of your code is opengl stuff. Every lib does it in 3d anyways, so get used to these aliases. The fun part begins just after that : we create a VAO and use it. VAOs are kind of hard to explain, but basically, they’re memory addresses that act like containers to a set of buffers. So the point is, you create and bind a VAO first to set its data, and at render time, you bind it back to automatically get the buffers you defined in it. And this, kids, is why it’s awesome. This is pure opengl though, it’s present in ES 2 and recent opengl implementations for computers, so enjoy. You might notice a difference on other platforms : here we call glGenVertexArraysOES which is specific to ES 2. Just remove the OES suffix and it’ll work just fine on your computer. The point of our 3 variables at the start is that we store the identifiers for reuse (and disposal). Next, we create our index and vertex buffers GPU side and upload our data. The steps are always the same : create the buffer, tell opengl to use it as a specific buffer (using the defined masks), upload our data. The upload method takes a few parameters : the mask for which buffer we’re aiming, the size in bytes of the data we’re sending, the actual data, and its usage. Usage is how to specify how the GPU will act with the data regarding CPU : if you plan to update your vertices often, you might want this to be dynamic. That’s not the case here, so we’ll set it as static. More info on the official docs for glBufferData. This operation is exactly the same for indices and vertices, appart from their specific masks.

The next part is kind of where I consider black magic usually happens with DirectX : layout. The point of layout is to tell your GPU how to interpret the vertex data you send. In DirectX, this gets me going all “wat” as it is totally counter intuitive. OpenGL in raw is a bit better, and GLKit makes it pretty simple. So first of all, we tell OpenGL that we want position activated for our shaders with glEnableVertexAttribArray. We use a GLKit define to simplify this, so it’s clearly too easy. The tricky part is defining which part of your data is actually position. As you should have noticed, we uploaded GLKVector3 on our GPU. It’s a structure made of 3 floats, and we didn’t normalize it, and all of our vector defines the position. So when we define our data structure with glVertexAttribPointer, it’s pretty simple : first, what we are defining (position), then the number of values defining our position, the type of value (using a gl define and not an alias type), a boolean to tell if it’s normalized data, the size in bytes of 1 vertex (aka the stride, since we are in a flat array GPU side), and the offset to the start of our data in the structure we sent the GPU. That’s it (yeah, that’s already a lot of stuff). We close the VAO so that OpenGL knows we’re not writing into it anymore.

So, how do we draw? We need a shader, or else nothing will ever show on our screen. But right now, I don’t feel like writing GLSL, doing a shader program structure, compiling stuff and uploading it to the GPU, etc… fortunately, GLKit provides a helper shader that’ll do most of what you’re looking to do with a shader : GLKBaseEffect. Right after our previous code, let’s define one (make the variable on the instance level, we’re going to need it later) :

    GLKBaseEffect* effect;
    effect = [[GLKBaseEffect alloc] init]; // init our effect
    // set up a constant color to draw with
    effect.useConstantColor = GL_TRUE;
    effect.constantColor = GLKVector4Make(0, 1, 0, 1);
    // set up "camera"
    effect.transform.projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(60), fabsf(view.bounds.size.height / view.bounds.size.width), 0.1f, 100.f);
    effect.transform.modelviewMatrix = GLKMatrix4MakeTranslation(0, 0, -5);

So, initialization is easy : create an instance of the class, and have fun with the properties. There’s a ton of them, including 3 lights, texture management, diffuse, emissive, specular color, etc… we’ll just be using a constant green color for the sake of simplicity. We’ll also set up our “camera” so that we can view our render from a certain distance. We make use of another GLKit structure : GLKMatrix4. There are many helper functions to create the projection and view matrices (google it if you don’t know what it means), so basically we set our projection to a 60° FOV, with the aspect ratio of our screen, and a near/far to [0.1f, 100.f[, which is totally arbitrary. Small fact that can save you five minutes of rage : during ViewDidLoad, view’s width and height don’t take orientation in account. So if you’re in landscape like I am, invert width and height for aspect ratio math. This is fixed during runtime, so it’s just for the sake of having a perfect square on the screen for this example. So now, to draw our stuff, we’ll go back to our draw method and right after the clear order, add this:

    glBindVertexArrayOES(vertexArray); // use our VAO
    [effect prepareToDraw]; // do secret mysterious apple stuff
    glDrawElements(GL_TRIANGLES,  6, GL_UNSIGNED_INT, 0); // draw
    glBindVertexArrayOES(0); // don't use our VAO

As I said previously, loading our VAO allows us to specify to the GPU we want to use all the associated data (index and vertex buffer). We spool the effect to prepare all the shadery stuff in the back (legend wants it that shader is actually generated realtime depending on the properties used) and draw our indexed elements : first we specify what we are drawing (triangles max, no quads for ES 2, sorry), how much indices we’re reading, the kind of data the indices are, and the offset, useless thanks to the VAO. We then unbind the VAO to prevent memory leak GPU side (it’s a best practice, it doesn’t necessarily happen). Run it, and you should see some ugly green square which a magenta background burning your eyes. Success! One last thing though : unsetting all the data. Add this override to ViewDidUnload :

-(void)viewDidUnload
{
    [super viewDidUnload];
   
    // delete our buffers GPU side
    glDeleteBuffers(1, &vertexBuffer);
    glDeleteBuffers(1, &indexBuffer);
    glDeleteVertexArraysOES(1, &vertexArray);
    // delete our context
    [EAGLContext setCurrentContext:context];
    if ([EAGLContext currentContext] == context)
        [EAGLContext setCurrentContext:nil];
    context = nil;
}

This is important because you need to remember OpenGL is C and not Objective-C. Meaning that resources don’t get automatically released and GPU side can keep the garbage for a long time. So save your iDevice, release OpenGL stuff.

Well, that’s it. It might seem like a lot of code, but if I showed you the same thing with DirectX, you’d be screaming most probably. Anyways, this is just an introduction, and it covers approximately nothing of what kind of benefits GLKit brings. It’s really a great piece of code for fast app development, and the great part is that you don’t have to use it if you don’t want : it’s pure helper over OpenGL ES 2, and sweetens the stuff without making it dependant! I’d recommend you have a look at Jeff Lamarche’s code if you want to get started and also the book “Learning OpenGL ES for iOS” which is an excellent way to go way deeper into this tech.

You can find the full code for ViewController.m on this Gist !

Have fun, code safe.

Tuxic