It's been a while since I last updated the blog. Lots of contract work and deadlines have made it difficult to keep up with the writing. Anyway, I have jotted down a couple of ideas for some posts, so here's the first one.
Lately I've been busy digging deeper into OpenGL (actually OpenGL ES) and GLSL. This is a very very big topic (books about these subjects are often just shy of a thousand pages) and I don't pretend to give a thorough explanation about them. What I'll try to achieve is to give a very high overview of the technology stack followed by a hands on tutorial of a very basic drawing (a 3D cube). You should end up understanding what are the basic concepts of OpenGL, why and when you should use it, and how you should configure an iOS app to use OpenGL technology together with GLKit (there's still a lack of good tutorials on integrating GLKit).
What is OpenGL?
OpenGL, which stands for Open Graphics Library, is a cross-language, multi-platform API for rendering 2D and 3D computer graphics. Here I'll be talking specifically about the ES (embedded system) version which is the one in use on mobile devices (iOS and Android). Just to give some context, the main competitor of OpenGL is D3D (or Direct3D), a Windows technology part of the well-known DirectX API. If you want to learn more about the history of the two competing technolgies and how they evolved, I strongly encourage reading this excellent StackExchange thread.
Simply put, OpenGL is a way of programming the GPU and it is at the heart of almost everything we see on a computer / smartphone / tablet screen nowadays. Even UIKit sits on top of OpenGL to do its drawing. It's just hidden and abstracted into more developer friendly methods, but that's the one technology behind graphics rendering.
In Apple's technology stack there are different frameworks to do different things. You should use CoreGraphics and CoreImage to deal with images and filters, CoreAnimation to animate views, SceneKit (at the moment on OS X only, but probably soon to be seen on iOS too) to manage 3D objects. So why and when should you use OpenGL. For short: when you want to be able to access the powerful features of the graphics card (GPU) of your device. And by the way, at the moment, OpenGL is the only way of displaying a 3D object in a native iOS app.
As a side note, if someone told you that OpenGL is the best suited and most powerful tool to make videogames, that's only part of the truth. Certainly OpenGL powers all modern games (except for those running on MSFT D3D), but hardly any of them (apart probably from big studios) are designed by writing code from scratch. And if you're interested in such topics, then you might want to check out other frameworks (whether 2D or 3D) that have a higher level of abstraction. I'll try and write a future post about some viable options for iOS developers like Cocos2D, Corona SDK and Unity3D.
So, let's focus on OpenGL ES. Most of the things we're going to cover also apply to OpenGL, some of them are specific to the embedded version. From now on when I'll say OpenGL, I'll mean the ES version. Let's see briefly a few key points that are at the heart of OpenGL:
- state machine: OpenGL is a state machine which means that when you set an option or you bind a buffer, that option or that buffer will be in use until you 'disable' them. As an example
glEnable(GL_DEPTH_TEST);will enable the depth testing feature of OpenGL (which helps deciding which elements of a rendered scene are visible, and which are hidden) and as such it will remain until explicitly disabled;
- coordinate system: OpenGL coordinate system is different from the one used by UIKit in that it has its origin centered on the screen. With an overly simplistic explantion, I'd say that by mapping a 3D space, its
x = 0, y = 0, z = 0coordinates will be at the center of your device's screen. Furthermore, without applying matrix transformations to the view, plane coordinates will go from (-1, -1) / lower left corner of the screen, to (1, 1) / upper right corner of the screen. More detailed information can be found here;
- vertices: vertices represent the data associated with the 3D objects you want OpenGL to render onscreen. Vertex data is what you need to load into the GPU so that the vertex shader can process it. Vertices are usually defined by their position (in a 3D space), color, normals and eventually associated texture;
- working with primitives: finally you have to tell OpenGL how to interpret the stream of data you passed into the sahders by definig how the primitives, i.e. points, lines and triangles, shoul dbe drawn (more on this in the upcoming tutorial).
OpenGL ES v. 1 vs v. 2
From iOS 3.0 onwards Apple supports OpenGL ES 2 and has strongly encouraged developers to shift towards the latest version of the graphics library (often encountering some resistance). The main difference between v.1 and v.2 is what it's called the programmable pipeline. Basically OpenGL ES 2 offers and requires you to define your own shaders (specific programs that are executed on the GPU instead of the CPU).
While in v.1 you could do away with providing to the GPU anything else than raw data, in v.2 you need to define your own shaders. Meaning: more power, more work for the developer. OpenGL ES v.2 in fact requires you to write:
- a vertex shader which applies computations (transformations) once for each vertex passed thru the pipeline;
- a fragment shader which applies color or texture data to the fragment, i.e. after the primitive has been rasterized.
Given that this was one of the main reasons developers were not moving towards v.2, Apple introduced - with iOS 5 - a new framework, GLKit.
This is going to be the last bit of theory before taking care of some real stuff. Suffice to say that GLKit provides some facilities to take care of some of the harder bits and repetitive OpenGL stuff. Apart from some useful math utilities to deal with matrices, quaternions, affine and homogeneous transformations and such other scaring entities, GLKit introduced
GLKBaseEffect, a class that takes away all the complexity of shaders. The tutorial we'll be focusing on in part 2 of this post (to come briefly) will show how to set up a
GLKViewController with an embedded
GLKView by making use of the base effect.