选择在OpenGL ES 2.0(iOS)中绘制的对象的最佳方法是什么?
我正在绘制点数.
解决方法
这里是颜色采摘的工作原型,在大多数旧的iPad上测试并且工作良好.这实际上是一个名为InCube Chess的项目的一部分,可以在应用商店中找到.您将看到的主要代码位于从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场景中的某些物体),你需要通过点击屏幕找到一张作品.也就是说,您需要将2d屏幕点击协调(在这种情况下为@point)转换为棋子实例.
每件作品都有其独特的名称,我称之为“印章”.您可以将密封件从1分配到某物.选择功能返回由tap coord找到的密封.然后盖上你可以很容易地找到你的碎片哈希表或阵列像这样的方式:
-(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.
现在这是主要的选择功能.请注意,我的glkview是反向的,您不能使用其缓冲区进行颜色选择.这意味着您需要创建自己的屏幕缓冲区,禁用抗锯齿功能,仅用于选择目的.
- (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,GL_RGBA,GL_UNSIGNED_BYTE,pixelColor); glDeleteRenderbuffers(1,&colorRenderbuffer); glDeleteFramebuffers(1,&framebuffer); return pixelColor[0]; }
这里是在上面的函数中使用的render()函数.请注意,为了渲染目的,它清除了具有一些背景颜色的缓冲区,并且为了选择大小写,它使其变黑,以便您可以轻松地检查您是否点击了任何片断.
- (void) render:(DrawMode)mode { if (mode == DM_RENDER) glClearColor(backgroundColor.r,backgroundColor.g,backgroundColor.b,1.0f); else glClearColor(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); } /* Actually normal render the piece using it geometry buffers. */ [self renderPiece:type]; [self popMatrix]; }
- (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 !!! */ } }
基本上是这样您将具有非常精确的拾取算法,远远优于光线跟踪或其他.
这里帮助推/弹矩阵的东西.
- (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设置/清理功能.
- (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); } /* !!! 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]; }
希望这有助于并可能关闭“挑选问题”永远:)