iOS上的OpenGL ES 2.0对象拾取

什么是select在OpenGL ES 2.0(iOS)中绘制的对象的最佳方法?

我正在绘制点。

这里是工作原理的颜色采摘,在大多数旧的ipad上testing和运作良好。 这实际上是一个名为InCube Chess的项目的一部分,可以在app store中find。 您将看到的主代码位于从GLKViewController派生的类中,如下所示:

@interface IncubeViewController : GLKViewController 

这意味着你有glkview:((GLKView *)self.view)。

这里也有一些属性:

 @property (strong, nonatomic) EAGLContext *context; @property (strong, nonatomic) GLKBaseEffect *effect; 

不要忘记在你的* .m文件中合成它们。

 @synthesize context = _context; @synthesize effect = _effect; 

这个想法是,你的桌子上有棋子(或者你的3D场景中的一些物体),你需要通过点击屏幕来find棋子列表。 也就是说,你需要将你的2D屏幕水龙头(在这种情况下是@point)转换为棋子实例。

每件作品都有其独特的标识,我称其为“印章”。 你可以把密封从1分配到某物。 selectfunction返回由水龙头发现的封条。 然后有了这个印章,你可以很容易地find你的作品哈希表或数组中的方式是这样的:

 -(Piece *)findPieceBySeal:(GLuint)seal { /* !!! Black background in off screen buffer produces 0 seals. This allows to quickly filter out taps that did not select anything (will be mentioned below) !!! */ if (seal == 0) return nil; PieceSeal *sealKey = [[PieceSeal alloc] init:s]; Piece *p = [sealhash objectForKey:sealKey]; [sealKey release]; return p; } 

“sealhash”是一个NSMutableDictionary。

现在这是主要的selectfunction。 请注意,我的glkview是antialised,你不能使用它的缓冲区颜色select。 这意味着您需要创build自己的屏幕缓冲区,禁用了抗锯齿function,仅用于拾取目的。

 - (NSUInteger)findSealByPoint:(CGPoint)point { NSInteger height = ((GLKView *)self.view).drawableHeight; NSInteger width = ((GLKView *)self.view).drawableWidth; Byte pixelColor[4] = {0,}; GLuint colorRenderbuffer; GLuint framebuffer; glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glGenRenderbuffers(1, &colorRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER, colorRenderbuffer); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { NSLog(@"Framebuffer status: %x", (int)status); return 0; } [self render:DM_SELECT]; CGFloat scale = UIScreen.mainScreen.scale; glReadPixels(point.x * scale, (height - (point.y * scale)), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelColor); glDeleteRenderbuffers(1, &colorRenderbuffer); glDeleteFramebuffers(1, &framebuffer); return pixelColor[0]; } 

请注意,该function考虑了显示比例(视网膜或新的iPad)。

这里是上面函数中使用的render()函数。 请注意,为了渲染的目的,它清除了一些背景颜色的缓冲区和select的情况下,它使黑色,这样你可以很容易地检查,如果你点击任何一块根本没有。

 - (void) render:(DrawMode)mode { if (mode == DM_RENDER) glClearColor(backgroundColor.r, backgroundColor.g, backgroundColor.b, 1.0f); else glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Draw all pieces. */ for (int i = 0; i < [model->pieces count]; i++) { Piece *p = [model->pieces objectAtIndex:i]; [self drawPiece:p mode:mode]; } } 

接下来是我们如何画这件作品。

 - (void) drawPiece:(Piece *)p mode:(DrawMode)mode { PieceType type; [self pushMatrix]; GLKMatrix4 modelViewMatrix = self.effect.transform.modelviewMatrix; GLKMatrix4 translateMatrix = GLKMatrix4MakeTranslation(p->drawPos.X, p->drawPos.Y, p->drawPos.Z); modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, translateMatrix); GLKMatrix4 rotateMatrix; GLKMatrix4 scaleMatrix; if (mode == DM_RENDER) { scaleMatrix = GLKMatrix4MakeScale(p->scale.X, p->scale.Y, p->scale.Z); } else { /* !!! Make the piece a bit bigger in off screen buffer for selection purposes so that we always sure that we tapped it correctly by finger.*/ scaleMatrix = GLKMatrix4MakeScale(p->scale.X + 0.2, p->scale.Y + 0.2, p->scale.Z + 0.2); } modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, scaleMatrix); self.effect.transform.modelviewMatrix = modelViewMatrix; type = p->type; if (mode == DM_RENDER) { /* !!! Use real pieces color and light on for normal drawing !!! */ GLKVector4 color[pcLast] = { [pcWhite] = whitesColor, [pcBlack] = blacksColor }; self.effect.constantColor = color[p->color]; self.effect.light0.enabled = GL_TRUE; } else { /* !!! Use piece seal for color. Important to turn light off !!! */ self.effect.light0.enabled = GL_FALSE; self.effect.constantColor = GLKVector4Make(p->seal / 255.0f, 0.0f, 0.0f, 0.0f); } /* Actually normal render the piece using it geometry buffers. */ [self renderPiece:type]; [self popMatrix]; } 

这是如何使用上面显示的function。

 - (IBAction) tapGesture:(id)sender { if ([(UITapGestureRecognizer *)sender state] == UIGestureRecognizerStateEnded) { CGPoint tap = [(UITapGestureRecognizer *)sender locationInView:self.view]; Piece *p = [self findPieceBySeal:[self findSealByPoint:tap]]; /* !!! Do something with your selected object !!! */ } } 

这基本上是这样。 你会有非常精确的采摘algorithm,比光线追踪或其他更好。

在这里帮助推/stream行matrix的东西。

 - (void)pushMatrix { assert(matrixSP < sizeof(matrixStack) / sizeof(GLKMatrix4)); matrixStack[matrixSP++] = self.effect.transform.modelviewMatrix; } - (void)popMatrix { assert(matrixSP > 0); self.effect.transform.modelviewMatrix = matrixStack[--matrixSP]; } 

这里还有我使用的glkview安装/清理function。

 - (void)viewDidLoad { [super viewDidLoad]; self.context = [[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2] autorelease]; if (!self.context) NSLog(@"Failed to create ES context"); GLKView *view = (GLKView *)self.view; view.context = self.context; view.drawableDepthFormat = GLKViewDrawableDepthFormat24; [self setupGL]; } - (void)viewDidUnload { [super viewDidUnload]; [self tearDownGL]; if ([EAGLContext currentContext] == self.context) [EAGLContext setCurrentContext:nil]; self.context = nil; } - (void)setupGL { [EAGLContext setCurrentContext:self.context]; self.effect = [[[GLKBaseEffect alloc] init] autorelease]; if (self.effect) { self.effect.useConstantColor = GL_TRUE; self.effect.colorMaterialEnabled = GL_TRUE; self.effect.light0.enabled = GL_TRUE; self.effect.light0.diffuseColor = GLKVector4Make(1.0f, 1.0f, 1.0f, 1.0f); } /* !!! Draw antialiased geometry !!! */ ((GLKView *)self.view).drawableMultisample = GLKViewDrawableMultisample4X; self.pauseOnWillResignActive = YES; self.resumeOnDidBecomeActive = YES; self.preferredFramesPerSecond = 30; glDisable(GL_DITHER); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glLineWidth(2.0f); /* Load pieces geometry */ [self loadGeometry]; } - (void)tearDownGL { drawReady = NO; [EAGLContext setCurrentContext:self.context]; [self unloadGeometry]; } 

希望这有助于可能永远closures“挑选问题”:)

基于上面的解决scheme,以下是我如何使用3D拾取的深度缓冲区和GLKVector3签名来检索密封:

  • 我所有的东西都被封印了
  • 我使用一个GLKVector3命名的颜色来检索读取像素的封条。 有了这个解决scheme,你可以存储255 * 255 * 255 = 16,581,375个对象。

在着色器(顶点或片段,如你所愿)

添加一个select布尔值,告诉你是否进行select

 uniform bool picking; 

和GLKVector3

 uniform vec3 color; 

如果布尔采摘被激活,那么颜色将是GLKVector3

 if(picking) { colorVarying = vec4(color, 1.0); } 

在ViewController中

从密封创buildGLKVector3的方法(当我创build一个新的3D对象时):

 -(GLKVector3)pack:(uint)seal { GLKVector3 hash; float r = seal % 255; float g = (seal / 255) % 255; float b = (seal / (255 * 255)) % 255; hash = GLKVector3Make(r/255, g/255, b/255); return hash; } 

而视图控制器代码,你从触摸位置获得像素,并从选定的颜色获得选定的印章:

 -(uint)getSealByColor:(GLKVector3)color { color = GLKVector3DivideScalar(color, 255); for (MyObject *o in _objects) { if(GLKVector3AllEqualToVector3(o.color, color)) { return o.seal; } } return 0; } -(void)tap:(UITapGestureRecognizer*)recognizer { CGPoint p = [recognizer locationInView:self.view]; GLKVector3 i = [self pickingAt:p]; _sealSelected = [self getSealByColor:i]; } -(GLKVector3)pickingAt:(CGPoint)position { CGFloat scale = [UIScreen mainScreen].scale; GLsizei w = self.view.bounds.size.width * scale; GLsizei h = self.view.bounds.size.height * scale; GLuint fb; GLuint rb; GLuint db; Byte pixelColor[4] = {0,}; glGenFramebuffers(1, &fb); glBindFramebuffer(GL_FRAMEBUFFER, fb); glGenRenderbuffers(1, &rb); glBindRenderbuffer(GL_RENDERBUFFER, rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, w, h); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); //here we also create a depth buffer for 3D objects picking glGenRenderbuffers(1, &db); glBindRenderbuffer(GL_RENDERBUFFER, db); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, w, h); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, db); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { NSLog(@"Framebuffer status: %x", (int)status); return GLKVector3Make(0.0, 0.0, 0.0); } //we render the scene with our picking boolean activated [self render:YES]; glReadPixels(position.x * scale, (h - (position.y * scale)), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelColor); glDeleteRenderbuffers(1, &db); glDeleteRenderbuffers(1, &rb); glDeleteFramebuffers(1, &fb); return GLKVector3Make(pixelColor[0], pixelColor[1], pixelColor[2]); } 

希望这可以帮助别人,这是我如何允许超过255个对象的颜色select。

Interesting Posts