GLKit Tutorial for iOS: Getting started with OpenGL ES

Learn how to use OpenGL ES in iOS in this GLKit tutorial. You’ll go from fresh project to spinning cube rendered using OpenGL and learn all the theory along the way! By Felipe Laso-Marsetti.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Tidying Up

You’ve created several buffers that need to be cleaned up. Add the following method in ViewController to do so:

private func tearDownGL() {
  EAGLContext.setCurrent(context)
    
  glDeleteBuffers(1, &vao)
  glDeleteBuffers(1, &vbo)
  glDeleteBuffers(1, &ebo)
        
  EAGLContext.setCurrent(nil)
        
  context = nil
}

With the code above, you:

  • Set the EAGLContext to your context — the one you’ve been working with this whole time.
  • Delete the VBO, EBO and VAO.
  • Nil out the context and also set the context variable to nil to prevent anything else from being done with it.

Now, add the following method:

deinit {
  tearDownGL()
}

This is the deinitializer, which simply calls the teardown method.

Build and run the project — notice the same gray screen? The thing about graphics programming and working with OpenGL is that it often requires a lot of initial setup code before you can see things.

Introducing GLKBaseEffect

Now it’s time for the next topic: Shaders.

Modern OpenGL uses what’s known as a programmable pipeline that gives developers full control of how each pixel is rendered. This gives you amazing flexibility and allows for some gorgeous scenes and effects to be rendered. The tradeoff, however, is that there’s more work for the programmer than in the past. Shaders are written in GLSL (OpenGL Shading Language) and need to be compiled before they can be used.

Here’s where GLKit comes to the rescue! With GLKBaseEffect, you don’t have to worry about writing shaders. It helps you achieve basic lighting and shading effects with little code.

To create an effect, add this variable to ViewController:

private var effect = GLKBaseEffect()

Then, add this line at the bottom of glkView(_ view:, drawIn rect:):

effect.prepareToDraw()

That single line of code binds and compiles shaders for you, and it does it all behind the scenes without writing any GLSL or OpenGL code. Pretty cool, huh? Build your project to ensure it compiles.

With your buffers and effects ready, you now need three more lines of code to tell OpenGL what to draw and how to draw it. Add the following lines right below the line you just added:

glBindVertexArrayOES(vao);
glDrawElements(GLenum(GL_TRIANGLES),     // 1
               GLsizei(Indices.count),   // 2
               GLenum(GL_UNSIGNED_BYTE), // 3
               nil)                      // 4
glBindVertexArrayOES(0)

Here’s what each method call does. The call to glBindVertexArrayOES binds (attaches) your VAO so that OpenGL uses it — and all its the setup and configuration — for the upcoming draw calls.

glDrawElements() is the call to perform drawing and it takes four parameters. Here’s what each of them does:

  1. This tells OpenGL what you want to draw. Here, you specify triangles by using the GL_TRIANGLES parameter cast as a GLenum.
  2. Tells OpenGL how many vertices you want to draw. It’s cast to GLsizei since this is what the method expects.
  3. Specifies the type of values contained in each index. You use GL_UNSIGNED_BYTE because Indices is an array of GLubyte elements.
  4. Specifies an offset within a buffer. Since you’re using an EBO, this value is nil.

The moment of truth has arrived! Time to build and run your project.

You’ll see something like this:

Not bad, but also not what you were expecting. This isn’t a square being drawn and it’s not rotating. What’s going on? Well, no properties were set on the GLKBaseEffect, specifically, the transform properties for the projection and model view matrices.

Setting Up the Geometry

Time for some theory…

A projection matrix is how you tell the GPU how to render 3D geometry on a 2D plane. Think of it as drawing a bunch of lines out from your eye through each pixel in your screen. The pixel that is drawn to the screen is determined by whatever the frontmost 3D object each line hits.

GLKit has some handy functions to set up a projection matrix. The one you’re going to use allows you to specify the field of view along the y-axis, the aspect ratio and the near and far planes.

The field of view is like a camera lens. A small field of view (e.g., 10) is like a telephoto lens — it magnifies images by “pulling” them closer to you. A large field of view (e.g., 100) is like a wide-angle lens — it makes everything seem farther away. A typical value to use for this is around 65-75.

The aspect ratio is the aspect ratio that you want to render to (e.g., the aspect ratio of the view). It uses this in combination with the field of view, which is for the y-axis, to determine the field of view along the x-axis.

The near and far planes are the bounding boxes for the “viewable” volume in the scene. If something is closer to the eye than the near plane, or further away than the far plane, it won’t be rendered.

Note: This is a common problem to run into – you try and render something, which doesn’t show up. One thing to check is that the object is actually between the near and far planes.

Add the following code to the bottom of glkViewControllerUpdate(_:):

// 1
let aspect = fabsf(Float(view.bounds.size.width) / Float(view.bounds.size.height))
// 2
let projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0), aspect, 4.0, 10.0)
// 3
effect.transform.projectionMatrix = projectionMatrix

Here’s what that does:

  1. Calculates the aspect ratio of the GLKView.
  2. Uses a built-in helper function in the GLKit math library to create a perspective matrix; all you have to do is pass in the parameters discussed above. You set the near plane to four units away from the eye, and the far plane to 10 units away.
  3. Sets the projection matrix on the effect’s transform property.

You need to set one more property on the effect — the modelViewMatrix. This is the transform that is applied to any geometry that the effect renders.

The GLKit math library, once again, comes to the rescue with some handy functions that make performing translations, rotations and scales easy, even if you don’t know much about matrix math. Add the following lines to the bottom of glkViewControllerUpdate(_ controller:):

// 1
var modelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, -6.0)
// 2
rotation += 90 * Float(timeSinceLastUpdate)
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, GLKMathDegreesToRadians(rotation), 0, 0, 1)
// 3
effect.transform.modelviewMatrix = modelViewMatrix

If you look back to where you set up the vertices for the square, remember that the z-coordinate for each vertex was 0. If you tried to render it with this perspective matrix, it wouldn’t show up because it’s closer to the eye than the near plane.

Here’s how you fix that with the code above:

  1. The first thing you need to do is move the objects backwards. In the first line, you use the GLKMatrix4MakeTranslation function to create a matrix that translates six units backwards.
  2. Next, you want to make the cube rotate. You increment an instance variable, which you’ll add in a second, that keeps track of the current rotation and use the GLKMatrix4Rotate method to change the current transformation by rotating it as well. It takes radians, so you use the GLKMathDegreesToRadians method for the conversion.
  3. Finally, you set the model view matrix on the effect’s transform property.

Finally, add the following property to the top of the class:

private var rotation: Float = 0.0

Build and run the app one last time and check out the results:

A rotating square! Perfect!