2012-09-24 12 views
5

Ho un problema con le viste openGL. Ho due viste OpenGL. La seconda vista viene aggiunta come sottoview alla vista principale. Le due viste opengl sono disegnate in due diversi contesti opengl. Devo catturare lo schermo con le due viste opengl.Problema con "renderincontext" con viste opengl

Il problema è che se provo a rendere una CAEAGLLayer in un contesto, come di seguito:

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]; 

non funziona. Se vedo il contesto (dato l'output come un'immagine), i contenuti nel livello opengl sono mancanti. Ma trovo la barra degli strumenti e le immagini 2d allegate alla vista, nell'immagine di output. Non sono sicuro del problema. Per favore aiuto.

risposta

5

Ho avuto un problema simile e ho trovato una soluzione molto più elegante. Fondamentalmente, si sottoclasse CAEAGLLayer e si aggiungerà la propria implementazione di renderInContext che richiede semplicemente alla vista OpenGL di eseguire il rendering dei contenuti usando glReadPixels. Il bello è che ora puoi chiamare renderInContext su qualsiasi livello nella gerarchia, e il risultato è uno screenshot perfettamente composto e dall'aspetto perfetto che include le tue viste OpenGL!

nostro renderInContext nel CAEAGLLayer sottoclasse è:

- (void)renderInContext:(CGContextRef)ctx 
{ 
    [super renderInContext: ctx]; 
    [self.delegate renderInContext: ctx]; 
} 

Poi, nella vista OpenGL sostituiamo layerClass in modo che restituisca la nostra sottoclasse invece del CAEAGLLayer plain vanilla:

+ (Class)layerClass 
{ 
    return [MyCAEAGLLayer class]; 
} 

Aggiungiamo un metodo nella vista per rendere effettivamente il contenuto della vista nel contesto. Si noti che questo codice DEVE essere eseguito dopo che la vista GL è stata sottoposta a rendering, ma prima di chiamare presentRenderbuffer in modo che il buffer di rendering contenga il frame. In caso contrario, l'immagine risultante sarà probabilmente vuota (si potrebbe notare un comportamento diverso tra il dispositivo e il simulatore su questo particolare problema).

- (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); 
} 

Infine, per catturare uno screenshot si usa renderInContext nella solita fasion. Ovviamente la bellezza è che non è necessario afferrare direttamente la vista OpenGL. È possibile afferrare uno dei superviews della vista OpenGL e ottenere uno screenshot composto che comprende la vista OpenGL insieme a qualsiasi altra cosa accanto ad essa o su di esso:

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

Sto provando il tuo approccio ma glReadPixels non sembra copiare dati nel mio buffer (la variabile "data" nell'esempio). Qualche idea sul perché questo potrebbe essere il caso? Il mio setup è un normale UIView il cui livello di supporto è un CAEAGLLayer. Quindi aggiungo UIImageView con un'immagine come figlio della vista con supporto GL. Quindi provo ad afferrare il contenuto di renderBuffer e il mio buffer è ancora tutti gli 0 come quando l'ho inizializzato con calloc. Ho anche provato a impostare il livello come memoria renderbuffer ma nulla. Se eseguo il normale renderingInContext, restituisco il contenuto come un'immagine. – SaldaVonSchwartz

0
+0

Grazie per la risposta. Ho due viste opzionali (trame) una sull'altra. Devo catturare il risultato di due visualizzazioni. Non sono molto sicuro, se il puntatore mi aiuta a ottenere ciò che voglio. – user862972

+0

Le due trame sono disegnate in due diversi contesti opengl. – user862972

+0

Esegui il codice una sola volta per ogni contesto, quindi unisci le due immagini in una di cui hai bisogno. – MrMage

1

Questa questione è già stata risolta, ma io volevo notare che la risposta di Idoogy è in realtà pericolosa e una scelta sbagliata per la maggior parte dei casi d'uso.

Invece di sottoclasse CAEAGLLayer e creare un nuovo oggetto delegato, è possibile utilizzare i metodi di delega esistenti che eseguono esattamente la stessa cosa. Ad esempio:

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

è un ottimo metodo da implementare nelle viste basate su GL. Puoi implementarlo nello stesso modo in cui suggerisce, usando glReadPixels: assicurati di impostare la proprietà Backed-backing sulla tua vista su YES, in modo che tu possa chiamare il metodo sopra in qualsiasi momento senza doversi preoccupare di essere stato invalidato da presentazione per la visualizzazione.

Suddivisione dei livelli CAEAGL di sottoclasse con la relazione del delegato UIView/CALayer esistente: nella maggior parte dei casi, l'impostazione dell'oggetto delegato sul livello personalizzato comporterà l'esclusione del proprio UIView dalla gerarchia della vista. Così, il codice come:

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

si tradurrà in un, a senso unico rapporto superview-visualizzazione secondaria strano, dal momento che i metodi delegato che si basa su UIView non saranno attuate. (La tua superview avrà comunque il sottolivello dalla tua vista personalizzata, però).

Quindi, invece di sottoclasse CAEAGLLayer, è sufficiente implementare alcuni dei metodi delegati. Mela stabilisce che per voi qui: https://developer.apple.com/library/ios/documentation/QuartzCore/Reference/CALayerDelegate_protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40012871

Tutto il meglio,

Sam

+0

Ho intenzione di inviare una risposta tardiva a questo: la sottoclassi di CAEAGLLayer è perfettamente sicura e comporterà un comportamento identico al 100%. Questo perché il modo in cui sottoclassiamo CAEAGLLayer non è inizializzandolo manualmente con la nostra classe, ma piuttosto fornendo la classe come layerClass per UIView (che, se leggi la mia risposta, vedrai il metodo che ho consigliato) . Questo assicura che l'inizializzazione sia fatta dal sistema, quindi tutto è ESATTAMENTE LO STESSO come sarebbe stato senza sottoclasse, delegati e tutto. – ldoogy