XCode`s project wizard gives us templates for creating Navigation-based application and OpenGL-based applications. But why wouldn`t we want to have both – an OpenGL rendered view inside the Navigation-based application? We can subclass the UIView class, modify it to support OpenGL rendering and then use it as a regular view in our application.

First, we have to change the view`s default backing layer to CAEAGLLayer. CAEAGLLayer is a wrapper for CoreAnimation surface and is fully compatible with OpenGL ES rendering. Backing layer is set thru view`s layerClass static method:

+ (Class)layerClass
{
    return [CAEAGLLayer class];
}

OpenGL ES commands are executed in a context that is defined in a EAGLContext class. An instance of EAGLContext class will hold all information needed to render the scene. To create an instance of EAGLContext, we have to decide on the OpenGL API version we will use. We can create an OpenGL ES 1.x rendering context using kEAGLRenderingAPIOpenGLES1 parameter or OpenGL ES2.x context using kEAGLRenderingAPIOpenGLES2, depending on what devices are we going to support.

m_glContext = [[EAGLContext alloc] initWithAPI:
                        kEAGLRenderingAPIOpenGLES1];
[EAGLContext setCurrentContext: m_glContext];

After we are done with OpenGL rendering, just set current context to nil:

[EAGLContext setCurrentContext:nil];

To actually present any rendered content in a view, we will bind renderbuffer to the view`s rendering layer. We can do that in layoutSubviews method.

- (void) layoutSubviews
{
    glBindRenderbufferOES(GL_RENDERBUFFER_OES,
                          m_renderbuffer);
    [m_glContext renderbufferStorage:GL_RENDERBUFFER_OES
                        fromDrawable:(CAEAGLLayer*)self.layer];

    [self setupRenderContext:nil];
}

We will set up two buffers for basic OpenGL functionality before rendering any content. A framebuffer object for all rendering operations and a renderbuffer which will hold the resulting frame.

glGenFramebuffersOES(1, &m_framebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_framebuffer);

glGenRenderbuffersOES(1, &m_renderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_renderbuffer);

We will attach the renderbuffer as an output buffer for content rendered in the framebuffer.

glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
                       GL_COLOR_ATTACHMENT0_OES,
                       GL_RENDERBUFFER_OES,
                       m_renderbuffer);

When we are done, we can free the buffers using glDelete* functions.

glDeleteFramebuffersOES(1, &m_framebuffer);
glDeleteRenderbuffersOES(1, &m_renderbuffer);

Everything is set up now, we can render anything into the framebuffer and it will show up in a view. Things are usually rendered at a constant frame rate in OpenGL. A simplest way to accomplish this on iOS is using a repeating timer with time interval set to a single frame duration, or 1/framerate.

m_timer = [NSTimer scheduledTimerWithTimeInterval:
                   (NSTimeInterval)(1.f/30.f)
            target:self
          selector:@selector(setupRenderContext:)
          userInfo:nil
           repeats:TRUE
                   ];

setupRenderContext method will bind the buffers for current frame and render the frame.

- (void) setupRenderContext:(id)userInfo
{
    [EAGLContext setCurrentContext:m_glContext];
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_framebuffer);

    [self render];

    glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_renderbuffer);
    [m_glContext presentRenderbuffer:GL_RENDERBUFFER_OES];
}

All we have to do now is to override the render method with any OpenGL rendering we might need.

0 comments