Tuve un problema similar y encontré una solución mucho más elegante. Básicamente, subclase CAEAGLLayer y agrega su propia implementación de renderInContext que simplemente le pide a la vista de OpenGL que represente los contenidos utilizando glReadPixels. La belleza es que ahora puedes invocar a renderInContext en cualquier capa de la jerarquía, y el resultado es una captura de pantalla completamente compuesta y de aspecto perfecto que incluye tus vistas de OpenGL.
Nuestra renderInContext en el CAEAGLLayer subclase es:
- (void)renderInContext:(CGContextRef)ctx
{
[super renderInContext: ctx];
[self.delegate renderInContext: ctx];
}
A continuación, en la vista OpenGL reemplazamos layerClass para que vuelva nuestra subclase en lugar de la llanura CAEAGLLayer vainilla:
+ (Class)layerClass
{
return [MyCAEAGLLayer class];
}
Añadimos un método en la vista para realmente procesar los contenidos de la vista en el contexto. Tenga en cuenta que este código DEBE ejecutarse después de que se haya procesado su vista GL, pero antes de llamar al presentRenderbuffer
para que el búfer de renderización contenga su fotograma. De lo contrario, la imagen resultante probablemente estará vacía (es posible que vea un comportamiento diferente entre el dispositivo y el simulador en este tema en particular).
- (void) renderInContext: (CGContextRef) context
{
GLint backingWidth, backingHeight;
// 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.
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
// Get the size of the backing CAEAGLLayer
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
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);
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
// 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);
CGFloat scale = self.contentScaleFactor;
NSInteger widthInPoints, heightInPoints;
widthInPoints = width/scale;
heightInPoints = height/scale;
// 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(context, kCGBlendModeCopy);
CGContextDrawImage(context, CGRectMake(0.0, 0.0, widthInPoints, heightInPoints), iref);
// Clean up
free(data);
CFRelease(ref);
CFRelease(colorspace);
CGImageRelease(iref);
}
Por último, con el fin de tomar una captura de pantalla que utilice renderInContext en el fasion habitual. Por supuesto, la belleza es que no necesita tomar la vista de OpenGL directamente. Puede tomar uno de los superviews de la vista OpenGL y obtener una captura de pantalla compuesto que incluye el punto de vista de OpenGL, junto con cualquier otra cosa a su lado o encima de ella:
UIGraphicsBeginImageContextWithOptions(superviewToGrab.bounds.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
[superviewToGrab.layer renderInContext: context]; // This recursively calls renderInContext on all the sublayers, including your OpenGL layer(s)
CGImageRef screenShot = UIGraphicsGetImageFromCurrentImageContext().CGImage;
UIGraphicsEndImageContext();
Estoy intentando su enfoque, pero glReadPixels no parece estar copiando ningún dato en mi búfer (la variable "datos" en su ejemplo). ¿Alguna idea de por qué este podría ser el caso? Mi configuración es una UIView regular cuya capa de respaldo es CAEAGLLayer. Luego agrego un UIImageView con una imagen como elemento secundario de la vista respaldada por GL. Luego trato de agarrar el contenido de renderBuffer y mi búfer sigue siendo todo 0 como cuando lo inicialicé con calloc. También intenté configurar la capa como almacenamiento de renderbuffer, pero nada. Si hago el renderInContext regular, recupero el contenido como una imagen. – SaldaVonSchwartz