Creating Render-to-Texture Secondary Framebuffer Objects on iOS Using OpenGL ES 2
I’ve come to greatly respect OpenGL and for which it stands as I discussed in my prior post. OpenGL was recently added to our awareness for a project for work at a time I was dabbling with it at home. These parallel lines of development brought excitement and mutual benefits for each locale. The project I am working on at work depends on what is inherently gaming technology even though you would think so to look at it.
I needed the ability to render the scene to a secondary framebuffer object (or FBO) due to the way the application incrementally draws a picture over time. Successive scenes merely draw the remaining portions whilst being merged with previous fragments rendered in the framebuffer. Think of the way Google Earth draws the image.
Anyway, the code worked fine on OpenGL 3.3 but for some reason the FBO failed the completeness test on OpenGL ES 2 under iOS. After much banging of head-to-wall I found the reason was due to the fact I was creating the secondary FBO ultimately during GLKViewController’s viewDidLoad call. This puzzled me as I already have a OpenGL context at this time due to a:
// We want OpenGL ES 2 thank-you very much
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
It seems creating a secondary framebuffer will fail if the size differs to the main framebuffer size. The best way to create secondary framebuffers is to wait for a viewWillLayoutSubviews notification. By this time your GLKit view is already resized so all you need to do is query the new size and create an appropriate FBO.
The above controller calls into the following view for creating the framebuffer
To draw, you must:
- Have the controller call into your view
- Bind to your secondary FBO
- Return to the controller
- Have the controller rebind to the main FBO
- Call into your view again to draw the offscreen texture
- The final step is to draw the screen quad (steps 5-6 above)
You might be wondering what all the bindDrawable is about. Well normally in OpenGL you would glBindFrameBuffer(…) to your framebuffer then perform a glBindFramebuffer (…, 0) to reset to the default framebuffer – well that won’t work on OpenGL ES (well you could perhaps if you knew the actual handle). You must use bindDrawable() instead.
Here are some books that helped me greatly