Drawing with GLKit

botptr picture botptr · Oct 18, 2011 · Viewed 14.5k times · Source

I am trying to write a game using opengl, but I am having a lot of trouble with the new glkit classes and the default template from iOS.

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

    if (!self.context) {
        NSLog(@"Failed to create ES context");
    }

    if(!renderer)
        renderer = [RenderManager sharedManager];
    tiles = [[TileSet alloc]init];

    GLKView *view = (GLKView *)self.view;
    view.context = self.context;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;

    [self setupGL];
}

- (void)setupGL
{
    int width = [[self view] bounds].size.width;
    int height = [[self view] bounds].size.height;

    [EAGLContext setCurrentContext:self.context];

    self.effect = [[GLKBaseEffect alloc] init];
    self.effect.light0.enabled = GL_TRUE;
    self.effect.light0.diffuseColor = GLKVector4Make(0.4f, 0.4f, 0.4f, 1.0f);

    //Configure Buffers
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

    glGenRenderbuffers(2, &colourRenderBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, colourRenderBuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, width, height);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colourRenderBuffer);

    glGenRenderbuffers(3, &depthRenderBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, depthRenderBuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderBuffer);

    //Confirm everything happened awesomely
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER) ;
    if(status != GL_FRAMEBUFFER_COMPLETE) {
        NSLog(@"failed to make complete framebuffer object %x", status);
    }

    glEnable(GL_DEPTH_TEST);

    // Enable the OpenGL states we are going to be using when rendering
    glEnableClientState(GL_VERTEX_ARRAY);

}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(0.4f, 0.4f, 0.4f, 1.0f);


    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);


    float iva[] = {
        0.0,0.0,0.0,
        0.0,1.0,0.0,
        1.0,1.0,0.0,
        1.0,0.0,0.0,
    };

    glVertexPointer(3, GL_FLOAT, sizeof(float) * 3, iva);

    glDrawArrays(GL_POINTS, 0, 4);

}
@end

With this the buffer clears(to a grey colour), but nothing from the vertex array renders. I have no idea what to do from here and due to the age of the technology there is not much information available on how to properly use glkit.

Answer

Stuart picture Stuart · Oct 19, 2011

I don't see anything in your setup code that loads your shaders - I presume you are doing this somewhere in your code?

In addition, in your setup code, you are creating your framebuffer. The GLKView does this for you - indeed you are telling the view to use a 24-bit depthbuffer in your viewDidLoad method:

GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;

So what your glkView:drawInRect: code above is doing is saying: "Bind my handmade framebuffer, and draw some stuff into it". The GLKView then automatically presents itself, but nothing has been drawn into it, you've only drawn into your handmade buffer. Unless you need additional framebuffer objects for tasks such as rendering to texture, then you don't need to concern yourself with framebuffer creation at all - let the GLKView do it automatically.

What you should be doing in your setupGL method (or anywhere you like in the setup) is creating your vertex array object(s) that remember the openGL state required to perform a draw. Then, in the glkView:drawInRect: method you should:

  1. Clear using glClear().
  2. Enable your program.
  3. Bind the vertex array object (or, if you didn't use a VAO, enable the appropriate vertex attrib pointers).
  4. Draw your data using glDrawArrays() or glDrawElements().

The GLKView automatically sets its context as current, and binds its framebuffer object before each draw cycle.

Perhaps try to think of GLKView more like a regular UIView. It handles most of the openGL code behind the scenes for you, leaving you to simply tell it what it needs to draw. It has its drawRect: code just like a regular UIView - with a regular UIView in drawRect: you just tell it what it should draw, for example using Core Graphics functions - you don't then tell it to present itself.

The GLKViewController is then best thought of as handling the mechanics of the rendering loop behind the scenes. You don't need to implement the timers, or even worry about pausing the animation on your application entering the background. You just need to override the update or glkViewControllerUpdate: method (depending on whether you're subclassing or delegating) to update the state of the openGL objects or view matrix.