title: iOS OpenGL ES 2.0: Offscreen render and save the result to an UIImage
body: <p>I'm using OpenGL ES to render some special effects, I don't want to show this to user, I just want to save the result as an UIImage, can anybody please help me?</p>
  
  <p>this is the code I'm using, I can get an image which contains the red clear color I use, but no geometry drawing shown.</p>
  
  <pre><code>#import "RendererGL.h"
  #import &lt;GLKit/GLKit.h&gt;
  #import &lt;UIKit/UIKit.h&gt;
  #import &lt;OpenGLES/EAGL.h&gt;
  #import &lt;OpenGLES/EAGLDrawable.h&gt;
  #import &lt;OpenGLES/ES2/glext.h&gt;
  #import &lt;QuartzCore/QuartzCore.h&gt;
  
  static NSInteger WIDTH_IN_PIXEL = 400;
  static NSInteger HEIGHT_IN_PIXEL = 300;
  
  typedef struct {
      GLKVector3 positionCoords;
  }
  SceneVertex;
  
  static const SceneVertex vertices[] =
  {
      {{-0.5f, -0.5f, 0.0}}, // lower left corner
      {{ 0.5f, -0.5f, 0.0}}, // lower right corner
      {{-0.5f,  0.5f, 0.0}}  // upper left corner
  };
  
  @implementation RendererGL
  {
      EAGLContext* _myContext;
  
      GLuint _framebuffer;
      GLuint _colorRenderbuffer;
      GLuint _depthRenderbuffer;
  
      GLuint _vertexBufferID;
  
      GLKBaseEffect *_baseEffect;
  }
  
  - (id) init
  {
      self = [super init];
      if (self)
      {
          _myContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
          [EAGLContext setCurrentContext:_myContext];
          [self setupOffscreenBuffer];
          [self setUpEffect];
          [self renderImage];
          [self saveImage];  //this do works, since I get an image, but the image only contains the red color I used to clear
      }
  
      return self;
  }
  
  -(void)setUpEffect
  {
      _baseEffect = [[GLKBaseEffect alloc] init];
      _baseEffect.useConstantColor = GL_TRUE;
      _baseEffect.constantColor = GLKVector4Make(0.0f, 0.0f, 1.0f,  1.0f);
  }
  
  //this code is from apples document
  -(void)setupOffscreenBuffer
  {
      glGenFramebuffers(1, &amp;_framebuffer);
      glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
  
      glGenRenderbuffers(1, &amp;_colorRenderbuffer);
      glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
      glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, WIDTH_IN_PIXEL, HEIGHT_IN_PIXEL);
      glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderbuffer);
  
      glGenRenderbuffers(1, &amp;_depthRenderbuffer);
      glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderbuffer);
      glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, WIDTH_IN_PIXEL, HEIGHT_IN_PIXEL);
      glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderbuffer);
  
      GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER) ;
      if(status != GL_FRAMEBUFFER_COMPLETE) {
          NSLog(@"failed to make complete framebuffer object %x", status);
      }
  }
  
  - (void) renderImage
  {
      GLenum error = GL_NO_ERROR;
  
      glClearColor(1, 0, 0, 1);   //red clear color, this can be seen
      glClear(GL_COLOR_BUFFER_BIT);
  
      glEnable(GL_DEPTH_TEST);
  
      [_baseEffect prepareToDraw];
  
      glGenBuffers(1, &amp;_vertexBufferID);
      glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferID);
      glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
      error = glGetError();
      if (error != GL_NO_ERROR) {
          NSLog(@"error happend, error is %d, line %d",error,__LINE__);
      }
  
      glEnableVertexAttribArray(GLKVertexAttribPosition);
      glVertexAttribPointer(GLKVertexAttribPosition,3,GL_FLOAT, GL_FALSE, sizeof(SceneVertex),  NULL);                   
      glDrawArrays(GL_TRIANGLES,0,3);
      error = glGetError();
      if (error != GL_NO_ERROR) {
          NSLog(@"error happend, error is %d, line %d",error,__LINE__);
      }
  
      glFinish();
      error = glGetError();
      if (error != GL_NO_ERROR) {
          NSLog(@"error happend, error is %d, line %d",error,__LINE__);
      }
  }
  
  -(void)saveImage
  {
      GLenum error = GL_NO_ERROR;
  
      NSInteger x = 0, y = 0;
      NSInteger dataLength = WIDTH_IN_PIXEL * HEIGHT_IN_PIXEL * 4;
      GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte));
  
      glPixelStorei(GL_PACK_ALIGNMENT, 4);
      glReadPixels(x, y, WIDTH_IN_PIXEL, HEIGHT_IN_PIXEL, GL_RGBA, GL_UNSIGNED_BYTE, data);
      NSData *pixelsRead = [NSData dataWithBytes:data length:dataLength];
  
      error = glGetError();
      if (error != GL_NO_ERROR) {
          NSLog(@"error happend, error is %d, line %d",error,__LINE__);
      }
  
      CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
      CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
      CGImageRef iref = CGImageCreate(WIDTH_IN_PIXEL, HEIGHT_IN_PIXEL, 8, 32, WIDTH_IN_PIXEL * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,
                                      ref, NULL, true, kCGRenderingIntentDefault);
  
  
      UIGraphicsBeginImageContext(CGSizeMake(WIDTH_IN_PIXEL, HEIGHT_IN_PIXEL));
      CGContextRef cgcontext = UIGraphicsGetCurrentContext();
      CGContextSetBlendMode(cgcontext, kCGBlendModeCopy);
      CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, WIDTH_IN_PIXEL, HEIGHT_IN_PIXEL), iref);
      UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  
      NSData *d = UIImageJPEGRepresentation(image, 1);
      NSString *documentDirPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
      static NSInteger imageNO = 1;
      imageNO++;
      NSString *savingPath = [documentDirPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%d.jpg",imageNO]];
      BOOL succ = [d writeToFile:savingPath atomically:NO];   //is succeeded
  
      UIGraphicsEndImageContext();
  
      free(data);
      CFRelease(ref);
      CFRelease(colorspace);
      CGImageRelease(iref);
  }
  
  @end
  </code></pre>