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 :
@interface 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 :
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; // context creation
// View setup
GLKView* view = (GLKView*)self.view;
view.context = context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
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.
glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
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 vertexBuffer; // our VBO for vertices
GLuint indexBuffer; // our VBO for indices
vertices = GLKVector3Make(-0.5f, -0.5f, 0); // create a new GLKVector3 structure
vertices = GLKVector3Make(0.5f, -0.5f, 0);
vertices = GLKVector3Make(0.5f, 0.5f, 0);
vertices = GLKVector3Make(-0.5f, 0.5f, 0);
indices = 0;
indices = 1;
indices = 2;
indices = 0;
indices = 2;
indices = 3;
glGenVertexArraysOES(1, &vertexArray); // create the VAO
glBindVertexArrayOES(vertexArray); // tell opengl we're using it
// create the vertices VBO
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
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// tell opengl to activate position on shaders...
// 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
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) :
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:
[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 :
// delete our buffers GPU side
// delete our context
if ([EAGLContext currentContext] == context)
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.