7

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

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

我有两个问题,我将非常感谢您的帮助:

  1. 我可以访问GLKViewController, GLKView, CAEAGLLayer( 的GLKView) 和EAGLContext. 我还可以访问所有与 OGLES 2.0 缓冲区相关的命令。如何结合这些来识别我在屏幕上点击的 EAGLContext 中像素的颜色?

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

我假设 (2) 的答案是“否”,但出于性能和不费力的代码修改的原因,我认为与更有经验的人核实是明智的。

任何建议将不胜感激。感谢您的时间

更新

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

尽管如此,关于后台缓冲区的任何提示将不胜感激。

4

1 回答 1

12

好吧,我已经弄清楚了如何尽可能简洁地做到这一点。下面我将解释如何实现这一点并列出所需的所有代码:)

为了允许触摸交互选择像素,首先将 a 添加UITapGestureRecognizer到您的GLKViewController子类(假设您想要点击选择像素),在该类中使用以下目标方法。您必须使您的GLKViewController子类 a UIGestureRecognizerDelegate

@interface GLViewController : GLKViewController <GLKViewDelegate, UIGestureRecognizerDelegate>

实例化手势识别器后,将其添加到view属性中(GLKViewController实际上是 a GLKView):

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

为您的手势识别器设置目标操作;您可以在使用特定创建它时执行此操作,init...但是我使用 Storyboard(又名“Xcode 4.2 中的新界面生成器”)创建我的并以这种方式连接它。

无论如何,这是我对点击手势识别器的目标操作:

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

在那里调用的 pick 方法是我在GLKViewController子类中定义的方法:

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

这利用了snapshotApple 提供的一种方便的新方法,从底层GLKView生成一个.UIImageEAGLContext

需要注意的是snapshotAPI 文档中的注释,其中指出:

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

这给了我一个线索,说明为什么我之前glReadPixels在尝试访问像素数据时调用的尝试生成了一个EXC_BAD_ACCESS, 以及让我走上正确道路的指示符。

您会注意到,在我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

我最初尝试了在 Apple API 文档中找到的一些相关代码,标题为:“Getting the pixel data from a CGImage context”,它需要 2 个方法定义而不是这个 1,但需要更多代码,并且有void *我使用的类型的数据无法实施正确的解释。

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

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

当然,您应该编写一些方法来提取这些 RGBA int 值(将在 0 - 255 范围内)并根据需要使用它们。UIColor一种方法是从上述方法返回 a ,实例化如下:

UIColor *color = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f];
于 2011-10-23T00:27:59.170 回答