iOS 5 + GLKView:如何访问基于颜色的顶点拾取的像素RGB数据?

时间:2011-10-22 17:25:06

标签: colors ios5 opengl-es-2.0 pixel picking

我一直在转换自己的个人OGLES 2.0框架,以利用新的iOS 5框架GLKit添加的功能。

在取悦结果之后,我现在希望实现here所述的基于颜色的拣选机制。为此,您必须访问后台缓冲区以检索触摸的像素RGBA值,然后将其用作顶点/基元/显示对象的唯一标识符。当然,这需要对所有顶点/基元/显示对象进行临时唯一着色。

我有两个问题,我非常感谢你们的帮助:

  
      
  1. 我可以访问GLKViewControllerGLKViewCAEAGLLayerGLKView)和EAGLContext。我也可以访问所有OGLES 2.0   缓冲相关命令。如何组合这些以识别颜色   我正在点击屏幕上的EAGLContext中的一个像素?

  2.   
  3. 鉴于我正在使用顶点缓冲区对象进行渲染,是否有一种巧妙的方法来覆盖提供给我的顶点着色器的颜色   首先不涉及修改缓冲顶点(颜色)   属性,其次不涉及添加IF   声明到顶点着色器?

  4.   

我认为(2)的答案是“不”,但出于性能和非艰苦的代码改造的原因,我认为与更有经验的人核实是明智的。

任何建议都会感激不尽。谢谢你的时间

更新

我现在知道如何使用glReadPixels从活动帧缓冲区读取像素数据。所以我想我只需要对后台缓冲区进行特殊的“独特颜色”渲染,短暂切换到它并读取像素,然后切换回来。这将不可避免地产生视觉闪烁,但我想这是最简单的方法;肯定比从屏幕快照创建CGImageContextRef并分析这种方式更快(也更明智)。

但是,关于后台缓冲区的任何提示都会非常感激。

1 个答案:

答案 0 :(得分:12)

好吧,我已尽可能简明扼要地研究如何做到这一点。下面我将解释如何实现这一点并列出所需的所有代码:)

为了允许触摸交互选择像素,首先向UITapGestureRecognizer子类添加GLKViewController(假设您需要点击选择像素),其中包含以下目标方法类。您必须将GLKViewController子类设为UIGestureRecognizerDelegate

@interface GLViewController : GLKViewController <GLKViewDelegate, UIGestureRecognizerDelegate>

在实例化您的手势识别器后,将其添加到view属性(GLKViewController中的GLKView实际上是// Inside GLKViewController subclass init/awakeFromNib: [[self view] addGestureRecognizer:[self tapRecognizer]]; [[self tapRecognizer] setDelegate:self]; ):

init...

设置手势识别器的目标操作;你可以在使用特定的-(IBAction)onTapGesture:(UIGestureRecognizer*)recognizer { const CGPoint loc = [recognizer locationInView:[self view]]; [self pickAtX:loc.x Y:loc.y]; } 创建它时执行此操作,但是我使用Storyboard(也称为“Xcode 4.2中的新界面生成器”)创建了它并以这种方式连接它。

无论如何,这是我的轻拍手势识别器的目标动作:

GLKViewController

我在-(void)pickAtX:(GLuint)x Y:(GLuint)y { GLKView *glkView = (GLKView*)[self view]; UIImage *snapshot = [glkView snapshot]; [snapshot pickPixelAtX:x Y:y]; } 子类中定义了一个我在其中定义的pick方法:

snapshot

这利用了一个方便的新方法GLKView,Apple在UIImage中包含了EAGLContext来从基础snapshot生成glReadPixels

需要注意的是EXC_BAD_ACCESS API文档中的注释,其中声明:

  

应该在您的应用程序明确调用时调用此方法   需要视图的内容;永远不要试图直接阅读   使用OpenGL ES函数的底层帧缓冲区的内容。

这让我得到了一个线索,为什么我之前尝试调用pickAtX:Y:尝试访问像素数据时尝试生成pickPixelAtX:Y:,以及指示我将其发送到正确的路径。

你会注意到我刚才定义的UIImage方法,我在UIImage上拨打@interface UIImage (NDBExtensions) -(void)pickPixelAtX:(NSUInteger)x Y:(NSUInteger)y; @end 。这是我在自定义类别中添加到@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 的方法:

void *

这是实施;这是所需的最终代码清单。代码来自this question,并根据收到的答案进行了修改:

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

我最初尝试在Apple API文档中找到的一些相关代码,标题为:“从CGImage上下文中获取像素数据”,这需要2个方法定义而不是1,但需要更多代码,并且数据类型为{ {1}}我无法实现正确的解释。

就是这样!将此代码添加到项目中,然后在点击像素时,它将以以下形式输出:

UIColor

当然,您应该编写一些方法来提取这些RGBA int值(将在0 - 255范围内)并根据需要使用它们。一种方法是从上面的方法返回UIColor *color = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f]; ,实例化如下:

{{1}}