2011-10-22 14 views
6

He estado convirtiendo mi propio marco personal OGLES 2.0 para aprovechar la funcionalidad agregada por el nuevo marco iOS 5 GLKit.iOS 5 + GLKView: ¿Cómo acceder a los datos RGB de píxeles para la selección de vértices basada en colores?

Después de resultados agradables, ahora deseo implementar el mecanismo de selección basado en el color descrito here. Para esto, debe acceder al búfer posterior para recuperar un valor de RGBA de píxel tocado, que luego se utiliza como un identificador único para un objeto de vértice/primitiva/visualización. Por supuesto, esto requiere una coloración temporal única de todos los vértices/primitivos/objetos de visualización.

tengo dos preguntas, y yo estaría muy agradecido por la asistencia, ya sea con:

  1. tengo acceso a un GLKViewController, GLKView, CAEAGLLayer (del GLKView) y un EAGLContext. También tengo acceso a todos los comandos relacionados con el buffer OGLES 2.0 . ¿Cómo los combino para identificar el color de un píxel en el EAGLContexto que estoy tocando en la pantalla?

  2. Dado que yo estoy usando Vertex Buffer Objects hacer mi traducción, hay una manera ordenada para anular el color proporcionado a mi sombreado de vértices la que en primer lugar no implique la modificación de vértice tamponada (color) atributos, y en segundo lugar, ¿no implica la adición de una declaración IF en el sombreador de vértices?

supongo que la respuesta a (2) es "no", pero por razones de rendimiento y código no renovar ardua pensé que aconsejable consultar con alguien con más experiencia.

Cualquier sugerencia será recibida con gratitud. Gracias por su tiempo

ACTUALIZACIÓN

Bueno, yo ahora saben leer datos de píxeles de la memoria intermedia de trama activa utilizando glReadPixels usted. Así que supongo que solo tengo que hacer el renderizado especial de "colores únicos" en el búfer posterior, cambiar brevemente a él y leer los píxeles, luego volver a encenderlo. Esto inevitablemente creará un parpadeo visual, pero supongo que es la manera más fácil; ciertamente más rápido (y más sensato) que crear un CGImageContextRef desde una instantánea de pantalla y analizar de esa manera.

Aún así, cualquier consejo sobre el buffer de la parte de atrás sería muy apreciado.

Respuesta

11

Bueno, he resuelto exactamente cómo hacerlo de la manera más concisa posible. A continuación explico cómo lograr esto y hacer una lista de todo el código necesario :)

Con el fin de permitir la interacción táctil para seleccionar un pixel, primero añadir un UITapGestureRecognizer a su GLKViewController subclase (asumiendo que usted quiere aprovechar al-select-pixel) , con el siguiente método objetivo dentro de esa clase.Debe realizar su GLKViewController subclase un UIGestureRecognizerDelegate:

@interface GLViewController : GLKViewController <GLKViewDelegate, UIGestureRecognizerDelegate> 

Después de crear instancias de su reconocedor gesto, añadirlo a la propiedad view (que en GLKViewController es en realidad un GLKView):

// Inside GLKViewController subclass init/awakeFromNib: 
[[self view] addGestureRecognizer:[self tapRecognizer]]; 
[[self tapRecognizer] setDelegate:self]; 

establecer la acción objetivo para su reconocedor de gestos; puedes hacerlo al crearlo usando un init... en particular, sin embargo, creé el mío usando Storyboard (también conocido como "el nuevo Interface Builder en Xcode 4.2") y lo conecté de esa manera.

De todos modos, aquí está mi acción objetivo para el reconocedor del grifo gesto:

-(IBAction)onTapGesture:(UIGestureRecognizer*)recognizer { 
    const CGPoint loc = [recognizer locationInView:[self view]]; 
    [self pickAtX:loc.x Y:loc.y]; 
} 

El método de selección denominado ahí es que yo he definido dentro de mi GLKViewController subclase:

-(void)pickAtX:(GLuint)x Y:(GLuint)y { 
    GLKView *glkView = (GLKView*)[self view]; 
    UIImage *snapshot = [glkView snapshot]; 
    [snapshot pickPixelAtX:x Y:y]; 
} 

Esto se aprovecha de un práctico nuevo método snapshot que Apple amablemente incluyó en GLKView para producir un UIImage del EAGLContext subyacente.

Lo importante tener en cuenta es un comentario en la documentación API snapshot, que establece:

Este método debe ser llamado siempre que su aplicación de forma explícita necesita el contenido de la vista; nunca intente leer directamente el contenido del framebuffer subyacente usando las funciones de OpenGL ES.

Esto me dio una pista de por qué mis anteriores intentos de invocar glReadPixels en los intentos de acceder a los datos de píxeles generaron un EXC_BAD_ACCESS, y el indicador que me envió por el camino correcto en su lugar.

Notarás en mi método pickAtX:Y: definido hace un momento que llamo a pickPixelAtX:Y: en el UIImage. Este es un método añadí a UIImage en una categoría personalizada:

@interface UIImage (NDBExtensions) 
-(void)pickPixelAtX:(NSUInteger)x Y:(NSUInteger)y; 
@end 

Aquí es la implementación; es el listado final del código requerido. El código de vino de this question y se ha modificado de acuerdo con la respuesta recibida no:

@implementation UIImage (NDBExtensions) 

- (void)pickPixelAtX:(NSUInteger)x Y:(NSUInteger)y { 

    CGImageRef cgImage = [self CGImage]; 
    size_t width = CGImageGetWidth(cgImage); 
    size_t height = CGImageGetHeight(cgImage); 

    if ((x < width) && (y < height)) 
    { 
     CGDataProviderRef provider = CGImageGetDataProvider(cgImage); 
     CFDataRef bitmapData = CGDataProviderCopyData(provider); 
     const UInt8* data = CFDataGetBytePtr(bitmapData); 
     size_t offset = ((width * y) + x) * 4; 
     UInt8 b = data[offset+0]; 
     UInt8 g = data[offset+1]; 
     UInt8 r = data[offset+2]; 
     UInt8 a = data[offset+3]; 
     CFRelease(bitmapData); 
     NSLog(@"R:%i G:%i B:%i A:%i",r,g,b,a); 
    } 
} 

@end 

originalmente intentó algún código relacionado que se encuentran en una documentación del API de Apple titulado: "Obtención de los datos de píxeles de un contexto CGImage", que requiere 2 definiciones de método en lugar de este 1, pero se requiere mucho más código y hay datos del tipo void * para los cuales no pude implementar la interpretación correcta.

Eso es todo! Añadir este código a su proyecto, a continuación, tras tocar un píxel es la salida que en la forma:

R:24 G:46 B:244 A:255 

Por supuesto, usted debe escribir algunos medios de extracción de esos valores RGBA int (que estarán en el rango de 0 - 255) y usándolos como quieras.Un método consiste en devolver un UIColor del método anterior, una instancia de este modo:

UIColor *color = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f]; 
+0

he terminado todo el mecanismo de separación ahora y funciona un regalo :) Hurra! – KomodoDave

+0

Esto realmente funciona brillantemente, gracias por publicar! – todd412

+0

De nada :) – KomodoDave

Cuestiones relacionadas