glReadPixels返回多个采样的零点

我正在写iOS的OpenGL应用程序,我需要在应用程序中呈现场景的屏幕截图。 当我不使用多重采样时,所有工作正常。 但是,当我开启多重采样时, glReadPixels不会返回正确的数据(场景绘制正确 – graphics质量比多重采样好得多)。

我已经在SO和其他一些地方检查了一些类似的问题,但是他们没有一个能够解决我的问题,因为我已经按照所提出的方式来做这件事了:

  1. 缓冲区解决后,但呈现呈现缓冲区之前,我正在采取截图。
  2. glReadPixels不会返回错误。
  3. 我甚至尝试将kEAGLDrawablePropertyRetainedBacking设置为YES并在缓冲区呈现后采取屏幕截图 – 也不起作用。
  4. 我支持OpenGLES 1.x渲染API(使用kEAGLRenderingAPIOpenGLES1初始化上下文)

基本上我不知道什么是错的。 在SO上发布问题是我的最后一招。

这是相关的源代码:

创build帧缓冲区

 - (BOOL)createFramebuffer { glGenFramebuffersOES(1, &viewFramebuffer); glGenRenderbuffersOES(1, &viewRenderbuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer]; glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); // Multisample support glGenFramebuffersOES(1, &sampleFramebuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, sampleFramebuffer); glGenRenderbuffersOES(1, &sampleColorRenderbuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, sampleColorRenderbuffer); glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, 4, GL_RGBA8_OES, backingWidth, backingHeight); glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, sampleColorRenderbuffer); glGenRenderbuffersOES(1, &sampleDepthRenderbuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, sampleDepthRenderbuffer); glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, 4, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight); glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, sampleDepthRenderbuffer); // End of multisample support if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) { NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); return NO; } return YES; } 

解决缓冲区部分并拍摄快照

  glBindFramebufferOES(GL_DRAW_FRAMEBUFFER_APPLE, viewFramebuffer); glBindFramebufferOES(GL_READ_FRAMEBUFFER_APPLE, sampleFramebuffer); glResolveMultisampleFramebufferAPPLE(); [self checkGlError]; //glFinish(); if (capture) captureImage = [self snapshot:self]; const GLenum discards[] = {GL_COLOR_ATTACHMENT0_OES,GL_DEPTH_ATTACHMENT_OES}; glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE,2,discards); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; 

快照方法(基本上从苹果文档复制)

 - (UIImage*)snapshot:(UIView*)eaglview { // Bind the color renderbuffer used to render the OpenGL ES view // If your application only creates a single color renderbuffer which is already bound at this point, // this call is redundant, but it is needed if you're dealing with multiple renderbuffers. // Note, replace "_colorRenderbuffer" with the actual name of the renderbuffer object defined in your class. glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); NSInteger x = 0, y = 0, width = backingWidth, height = backingHeight; NSInteger dataLength = width * height * 4; GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte)); // Read pixel data from the framebuffer glPixelStorei(GL_PACK_ALIGNMENT, 4); [self checkGlError]; glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); [self checkGlError]; // Create a CGImage with the pixel data // If your OpenGL ES content is opaque, use kCGImageAlphaNoneSkipLast to ignore the alpha channel // otherwise, use kCGImageAlphaPremultipliedLast CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL); CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); CGImageRef iref = CGImageCreate(width, height, 8, 32, width * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast, ref, NULL, true, kCGRenderingIntentDefault); // OpenGL ES measures data in PIXELS // Create a graphics context with the target size measured in POINTS NSInteger widthInPoints, heightInPoints; if (NULL != UIGraphicsBeginImageContextWithOptions) { // On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration // Set the scale parameter to your OpenGL ES view's contentScaleFactor // so that you get a high-resolution snapshot when its value is greater than 1.0 CGFloat scale = eaglview.contentScaleFactor; widthInPoints = width / scale; heightInPoints = height / scale; UIGraphicsBeginImageContextWithOptions(CGSizeMake(widthInPoints, heightInPoints), NO, scale); } else { // On iOS prior to 4, fall back to use UIGraphicsBeginImageContext widthInPoints = width; heightInPoints = height; UIGraphicsBeginImageContext(CGSizeMake(widthInPoints, heightInPoints)); } CGContextRef cgcontext = UIGraphicsGetCurrentContext(); // UIKit coordinate system is upside down to GL/Quartz coordinate system // Flip the CGImage by rendering it to the flipped bitmap context // The size of the destination area is measured in POINTS CGContextSetBlendMode(cgcontext, kCGBlendModeCopy); CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, widthInPoints, heightInPoints), iref); // Retrieve the UIImage from the current context UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // Clean up free(data); CFRelease(ref); CFRelease(colorspace); CGImageRelease(iref); return image; } 

通过在将glResolveMultisampleFramebufferAPPLE绑定为draw framebuffer并将sampleFramebuffer为read framebuffer之后执行glResolveMultisampleFramebufferAPPLE ,像往常一样解决多采样缓冲区。 但是,你是否还记得绑定viewFramebuffer作为读帧缓冲区( glBindFramebuffer(GL_READ_FRAMEBUFFER, viewFramebuffer) ),然后在glReadPixelsglReadPixels将始终从当前绑定的读取帧缓冲区读取,如果在多重采样parsing后没有更改此绑定,则这仍然是多重采样帧缓冲区,而不是默认值。

我还发现你的glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer)很让人恼火,因为这没有什么意义,当前绑定的渲染缓冲区只与在渲染缓冲区上工作的函数有关(实际上只有glRenderbufferStorage )(但也可能是ES做了一些有意义的事情,绑定它是[context presentRenderbuffer:GL_RENDERBUFFER_OES]工作所必需的)。 但是也许你认为这个绑定也控制着glReadPixels将读取的缓冲区,但事实并非如此,它总是从绑定到GL_READ_FRAMEBUFFER的当前framebufferGL_READ_FRAMEBUFFER