2012-09-24 11 views
5

Tengo un problema con las vistas de OpenGL. Tengo dos vistas OpenGL. La segunda vista se agrega como una subvista a la vista principal. Las dos vistas de OpenGL se dibujan en dos contextos de OpenGL diferentes. Necesito capturar la pantalla con las dos vistas opengl.Problema con "renderincontext" con vistas abiertas

La cuestión es que si trato de hacer una CAEAGLLayer en un contexto de la siguiente manera:

CGContextRef context = UIGraphicsGetCurrentContext(); 
CGContextTranslateCTM(context, 1*(self.frame.size.width*0.5), 1*(self.frame.size.height*0.5)); 
CGContextScaleCTM(context, 3, 3); 
CGContextTranslateCTM(context, abcd, abcd); 

CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.myOwnView.layer; 
[eaglLayer renderInContext:context]; 

no funciona. Si veo el contexto (dado el resultado como una imagen), los contenidos en la capa opengl faltan. Pero encuentro la barra de herramientas y las imágenes 2d adjuntas a la vista, en la imagen de salida. No estoy seguro del problema. Por favor ayuda.

Respuesta

5

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(); 
+0

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

0
+0

Gracias por la respuesta. Tengo dos vistas abiertas (texturas) una sobre la otra. Necesito capturar la resultante de dos vistas. No estoy muy seguro, si el puntero me ayuda a obtener lo que quiero. – user862972

+0

Las dos texturas se dibujan en dos contextos de OpenGL diferentes. – user862972

+0

Simplemente ejecuta el código una vez para cada contexto, luego combina las dos imágenes en una como lo necesites. – MrMage

1

Esta cuestión ya ha sido resuelta, pero Quería notar que la respuesta de Idoogy es realmente peligrosa y una mala elección para la mayoría de los casos de uso.

En lugar de la subclase CAEAGLLayer y crear un nuevo objeto de delegado, puede usar los métodos delegados existentes que logran exactamente lo mismo. Por ejemplo:

- (void) drawLayer:(CALayer *) layer inContext:(CGContextRef)ctx; 

es un excelente método para implementar en sus vistas basadas en GL. Puede implementarlo de la misma manera que sugiere, usando glReadPixels: simplemente asegúrese de establecer la propiedad de retención de respaldo en su vista a SÍ, para que pueda llamar al método anterior en cualquier momento sin tener que preocuparse de que haya sido invalidado por presentación para exhibición.

Subclassing La capa CAEAGL se confunde con la relación de delegado UIView/CALayer existente: en la mayoría de los casos, establecer el objeto delegado en su capa personalizada hará que su UIView se excluya de la jerarquía de vista. Por lo tanto, un código como:

customLayerView = [[CustomLayerView alloc] initWithFrame:someFrame]; 
[someSuperview addSubview:customLayerView]; 

dará lugar a una relación supervista-subvista de un modo extraño, ya que no se llevarán a cabo los métodos de delegado que se basa en UIView. (Sin embargo, su supervista aún tendrá la subcapa desde su vista personalizada).

Por lo tanto, en lugar de crear subcategorías de CAEAGLLayer, simplemente implemente algunos de los métodos delegados. Manzana establece que para usted aquí: https://developer.apple.com/library/ios/documentation/QuartzCore/Reference/CALayerDelegate_protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40012871

Todo lo mejor,

Sam

+0

Voy a publicar una respuesta tardía a esto: Subclassing the CAEAGLLayer es perfectamente seguro y dará como resultado un comportamiento 100% idéntico. Esto se debe a que la forma en que subclasimos CAEAGLLayer no es inicializándola manualmente con nuestra propia clase, sino que proporcionamos la clase como layerClass para UIView (que, si lee mi respuesta, verá el método que recomendé) . Esto asegura que la inicialización es hecha por el sistema, y ​​entonces todo es EXACTAMENTE EL MISMO que lo hubiera sido sin la subclase, delegados y todo. – ldoogy