Beginning OpenGL ES 2.0 with GLKit Part 1

A tutorial to get you up-to-speed with the basics of using OpenGL with GLKit, even if you have no experience whatsoever. By Ray Wenderlich.

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

GLKView Properties and Methods

We only set a few properties of GLKView here (context and delegate), but I wanted to mention the other properties and methods on GLKView that might be useful to you in the future.

This is an optional reference section and is for informational purposes only. If you want to keep coding away, feel free to skip to the next section! :]

context and delegate

We already covered these in the previous section, so I won’t repeat here.

drawableColorFormat

Your OpenGL context has a buffer it uses to store the colors that will be displayed to the screen. You can use this property to set the color format for each pixel in the buffer.

The default value is GLKViewDrawableColorFormatRGBA8888, which means 8 bits are used for each color component in the buffer (so 4 bytes per pixel). This is nice because it gives you the widest possible range of colors to work with, which often makes your app look nicer.

But if your app can get away with a lower range of colors, you might want to switch this to GLKViewDrawableColorFormatRGB565, which makes your app consume less resources (memory and processing tieme).

drawableDepthFormat

Your OpenGL context can also optionally have another buffer associated with it called the depth buffer. This helps make sure that objects closer to the viewer show up in front of objects farther away.

The way it works by default is OpenGL stores the closest object to the viewer at each pixel in a buffer. When it goes to draw a pixel, it checks the depth buffer to see if it’s already drawn something closer to the viewer, and if so discards it. Otherwise, it adds it to the depth buffer and the color buffer.

You can set this property to choose the format of the depth buffer. The default value is GLKViewDrawableDepthFormatNone, which means that no depth buffer is enabled at all.

But if you want this feature (which you usually do for 3D games), you should choose GLKViewDrawableDepthFormat16 or GLKViewDrawableDepthFormat24. The tradeoff here is with GLKViewDrawableDepthFormat16 your app will use less resources, but you might have rendering issues when objects are very close to each other.

drawableStencilFormat

Another optional buffer your OpenGL context can have is the stencil buffer. This helps you restrict drawing to a particular portion of the screen. It’s often useful for things like shadows – for example you might use the stencil buffer to make sure the shadows to be cast on the floor.

The default value for this property is GLKViewDrawableStencilFormatNone, which means there is no stencil buffer, but you can enable it by setting it to the only alternative – GLKViewDrawableStencilFormat8.

drawableMultisample

The last optional buffer you can set up through a GLKView property is the multisampling buffer. If you ever try drawing lines with OpenGL and notice “jagged lines”, multisampling can help with this issue.

Basically what it does is instead of calling the fragment shader one time per pixel, it divides up the pixel into smaller units and calls the fragment shader multiple times at smaller levels of detail. It then merges the colors returned, which often results in a much smoother look around edges of geometry.

Be careful about setting this because it requires more processing time and meomry for your app. The default value is GLKViewDrawableMultisampleNone, but you can enable it by setting it to the only alternative – GLKViewDrawableMultisample4X.

drawableHeight/drawableWidth

These are read-only properties that indicate the integer height and width of your various buffers. These are based on the bounds and contentSize of the view – the buffers are automatically resized when these change.

snapshot

This is a handy way to get a UIImage of the view’s current context.

bindDrawable

OpenGL has yet another buffer called a frame buffer, which is basically a collection of all the other buffers we talked about (color buffer, depth buffer, stencil buffer etc).

Before your glkView:drawInRect is called, GLKit will bind to the frame buffer it set up for you behind the scenes. But if your game needs to change to a different frame buffer to perform some other kind of rendering (for example, if you’re rendering to another texture), you can use the bindDrawable method to tell GLKit to re-bind back to the frame buffer it set up for you.

deleteDrawable

GLKView and OpenGL take a substantial amount of memory for all of these buffers. If your GLKView isn’t visible, you might find it useful to deallocate this memory temporarily until it becomes visible again. If you want to do this, just use this method!

Next time the view is drawn, GLKView will automatically re-allocate the memory behind the scenes. Quite handy, eh?

enableSetNeedsDisplay and display

I don’t want to spoil the surprise – we’ll explain these in the next section! :]

Updating the GLKView

Let’s try to update our GLKView periodically, like we would in a game. How about we make the screen pulse from red to black, kind of like a “Red Alert” effect!

Go to the top of AppDelegate.m and modify the @implementation line to add two private variables as follows:

@implementation AppDelegate {
    float _curRed;
    BOOL _increasing;
}

And initialize these in application:didFinishLaunchingWithOptions:

_increasing = YES;
_curRed = 0.0;

Then go to the glkView:drawInRect method and update it to the following:

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    
    if (_increasing) {
        _curRed += 0.01;
    } else {
        _curRed -= 0.01;
    }
    if (_curRed >= 1.0) {
        _curRed = 1.0;
        _increasing = NO;
    }
    if (_curRed <= 0.0) {
        _curRed = 0.0;
        _increasing = YES;
    }
    
    glClearColor(_curRed, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    
}

Every time drawInRect is called, it updates the _curRed value a little bit based on whether it's increasing or decreasing. Note that this code isn't perfect, because it doesn't take into effect how long it takes between calls to drawInRect. This means that the animation might be faster or slower based on how quickly drawInRect is called. We'll discuss a way to fix this later in the tutorial.

Compile and run and... wait a minute, nothing's happening!

By default, the GLKView only updates itself on an as-needed basis - i.e. when views are first shown, the size changes, or the like. However for game programming, you often need to redraw every frame!

We can disable this default behavior of GLKView by setting enableSetNeedsDisplay to false. Then, we can control when the redrawing occurs by calling the display method on GLKView whenever we want to update the screen.

Ideally we would like to synchronize the time we render with OpenGL to the rate at which the screen refreshes.

Luckily, Apple provides an easy way for us to do this with CADisplayLink! It’s really easy to use so let’s just dive in. First add this import to the top of AppDelegate.m:

#import <QuartzCore/QuartzCore.h>

Then add these lines to application:didFinishLaunchingWithOptions:

view.enableSetNeedsDisplay = NO;
CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

Then add a new render function as follows:

- (void)render:(CADisplayLink*)displayLink {
    GLKView * view = [self.window.subviews objectAtIndex:0];
    [view display];
}

Compile and run, and you should now see a cool pulsating "red alert" effect!