iOS上的OpenGL ES 2.0对象选取 [英] OpenGL ES 2.0 Object Picking on iOS

查看:112
本文介绍了iOS上的OpenGL ES 2.0对象选取的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我正在绘制点。

解决方案

以下是颜色拣选的原型,在大多数旧的ipads上测试并且运行良好。这实际上是可以在应用商店中找到的名为InCube Chess的项目的一部分。您将看到的主要代码位于从GLKViewController派生的类中,如下所示:

  @interface IncubeViewController:GLKViewController 

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



以下是一些属性:

  @property(强,非原子)EAGLContext * context; 
@property(强,非原子)GLKBaseEffect *效果;

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

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

想法是你的桌子上有棋子(或者3d场景中的某些物体)和你需要通过点击屏幕在你的作品列表中找到一块。也就是说,你需要将你的2d屏幕点击坐标(在这种情况下为@point)转换为棋子实例。



每件作品都有其唯一的ID我称之为密封。您可以将密封从1分配到某个东西。选择功能返回由龙头线圈找到的零件密封。然后有了密封,你可以很容易地找到你的作品哈希表或数组,如下所示:

   - (件* )findPieceBySeal:(GLuint)封印
{
/ * !!!屏幕缓冲区中的黑色背景产生0个密封。这允许
快速过滤掉没有选择任何东西的水龙头(将是下面提到的
)! * /
if(seal == 0)
return nil;
PieceSeal * sealKey = [[PieceSeal alloc] init:s];
Piece * p = [sealhash objectForKey:sealKey];
[sealKey发布];
返回p;
}

sealhash是一个NSMutableDictionary。



现在这是主要的选择功能。请注意,我的glkview是antialised,你不能使用它的缓冲区进行颜色选择。这意味着您需要创建自己的屏幕外缓冲区,禁用抗锯齿仅用于拣选目的。

   - (NSUInteger)findSealByPoint :( CGPoint)指向
{
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);
返回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);

返回pixelColor [0];
}

请注意,该功能会考虑显示比例(视网膜或新iPad)。 / p>

这是上面函数中使用的render()函数。请注意,出于渲染目的,它清除了具有一些背景颜色的缓冲区,并且为了选择它使其变为黑色,以便您可以轻松检查是否完全敲击了任何一块。

   - (void)渲染:(DrawMode)模式
{
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);

/ *画出所有作品。 * /
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);
}其他{
/ * !!!选择
目的是让屏幕缓冲区中的块更大一些,以便我们始终确保通过
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){
/ * !!!使用真实的颜色和灯光进行正常绘图! * /
GLKVector4颜色[pcLast] = {
[pcWhite] = whitesColor,
[pcBlack] = blacksColor
};
self.effect.constantColor = color [p-> color];
self.effect.light0.enabled = GL_TRUE;
}其他{
/ * !!!使用片密封颜色。重要的是关灯! * /
self.effect.light0.enabled = GL_FALSE;
self.effect.constantColor = GLKVector4Make(p-> seal / 255.0f,
0.0f,0.0f,0.0f);
}

/ *实际上,使用几何缓冲区正常渲染该块。 * /
[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]];

/ * !!!用你选择的对象做点什么! * /
}
}

基本上就是这样。你将拥有非常精确的拾取算法,它比光线追踪或其他更好。



这里是推/弹矩阵事物的助手。

   - (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(@创建ES上下文失败);

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);
}

/ * !!!绘制抗锯齿几何! * /
((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);

/ *加载件几何* /
[self loadGeometry];
}

- (无效)tearDownGL
{
drawReady = NO;
[EAGLContext setCurrentContext:self.context];
[self unloadGeometry];
}

希望这会有所帮助,并可能永远关闭挑选问题:)


What is the best method to select objects that have been drawn in OpenGL ES 2.0 (iOS)?

I am drawing points.

解决方案

Here is working prototype of color picking, tested on most old ipads and working well. This is actually some part of project called InCube Chess that one may find in app store. The main code you will see is located in a class derived from GLKViewController like this:

@interface IncubeViewController : GLKViewController

This means you have glkview in it: ((GLKView *)self.view).

Here are also some properties:

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

Don't forget to synthesize them in your *.m file.

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

The idea is that you have chess pieces on your table (or some objects in your 3d scene) and you need to find a piece in your list of pieces by tapping on a screen. That is, you need to convert your 2d screen tap coords (@point in this case) to chess piece instance.

Each piece has its unique id that I call a "seal". You can allocate the seals from from 1 up to something. Selection function returns piece seal found by tap coords. Then having the seal you can easily find your piece in pieces hash table or array in a way like this:

-(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" is a NSMutableDictionary.

Now this is the main selection function. Note, that my glkview is antialised and you can't use its buffers for color picking. This mean you need to create your own off screen buffer with antialiasing disabled for picking purposes only.

- (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];
}

Note that function takes into account display scale (retina or new iPads).

Here is render() function used in function above. Note, that for rendering purposes it clear s the buffer with some background color and for selecting case it makes it black so that you can easily check if you tapped on any piece at all or not.

- (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];
        }
}

Next is how we draw the piece.

- (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];
}

This is how to use the functions shown above.

- (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 !!! */
        }
}

This is basically it. You will have very precise picking algorithm that is much better than ray tracing or others.

Here helpers for push/pop matrix things.

- (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];
}

Here also glkview setup/cleanup functions that I used.

- (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];
}

Hope this helps and may be closes "the picking question" forever :)

这篇关于iOS上的OpenGL ES 2.0对象选取的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆