【问题标题】:opengl iOS glkit color pickingopengl iOS glkit 颜色拾取
【发布时间】:2013-10-04 22:00:04
【问题描述】:

它是关于使用颜色拾取来拾取 3d 对象。我将我的网格(更新:它驻留在静态头文件中,并且在同一个项目中的屏幕上很好地显示)绘制到一个新的屏幕外帧缓冲区,然后我使用 glReadPixels 来识别用户是否触摸它。

按照 Erik M.Buck 的“Learning OpenGLES for iOS”一书中项目 opengles_ch10_1 中的代码,我编写了以下代码,该代码始终打印为 nslog 拾取(通过点击)像素的颜色 0,0,0 .

我的代码是:

- (IBAction) tapGesture:(id)sender
{
   if ([(UITapGestureRecognizer *)sender state] == UIGestureRecognizerStateEnded) {

       CGPoint tapLocation = [(UITapGestureRecognizer *)sender locationInView:self.view];

        int tt = [self findMeshByPoint:tapLocation];

        //NSLog( @"tap value: %i", tt );
    }
}

- (NSUInteger)findMeshByPoint:(CGPoint)point
{
    //In openGL the y axis starts from the bottom of the screen

    point.y = self.view.bounds.size.height - point.y;

    GLKView *glView = (GLKView *)self.view;

    NSAssert([glView isKindOfClass:[GLKView class]],
             @"View controller's view is not a GLKView");

    // Make the view's context current

    [EAGLContext setCurrentContext:glView.context];

    glBindVertexArrayOES(0);

//        self.effect.constantColor = GLKVector4Make( 1.0f,  //This should be meshId/255.0f

    if(0 == _glVertexAttributeBufferID)
    {
        GLuint  glName;

        glGenBuffers(1,                // STEP 1

                     &glName);

        glBindBuffer(GL_ARRAY_BUFFER,  // STEP 2
                     glName);

        glBufferData(                  // STEP 3

                     GL_ARRAY_BUFFER,  // Initialize buffer contents

                    sizeof(parparit51OBJVertices), parparit51OBJVertices,

                     GL_STATIC_DRAW);            // Hint: cache in GPU memory

        _glVertexAttributeBufferID = glName;

    }
    else
    {
        glBindBuffer(GL_ARRAY_BUFFER,

                     _glVertexAttributeBufferID);
    }

    //glEnableVertexAttribArray(TETerrainPositionAttrib);

    glEnableVertexAttribArray( GLKVertexAttribPosition );

     if(0 == _program)
     {
            [self loadShadersWithName:@"UtilityPickTerrainShader"];

            [self buildFBO];

            NSAssert(0 != _program,

                     @"prepareOpenGL failed to load shaders");
        }

        glUseProgram(_program);

        // Pre-calculate the mvpMatrix

        GLKMatrix4 modelViewProjectionMatrix2 =

        GLKMatrix4Multiply(

                           self.effect.transform.projectionMatrix,

                           self.effect.transform.modelviewMatrix);

        // Standard matrices

        glUniformMatrix4fv(uniforms[UtilityPickTerrainMVPMatrix], 1, 0,

                           modelViewProjectionMatrix2.m);

//        glUniform2fv(uniforms[UtilityPickTerrainDimensionFactors], 1,

//                     &_temp1 ); //self.factors.v);  //I removed this from the shaders

        glUniform1f(uniforms[UtilityPickTerrainModelIndex],

                    1.0f ); // self.modelIndex / 255.0f);

    glBindFramebuffer(GL_FRAMEBUFFER, _pickFBO);

    glViewport(0, 0, 512, 512);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //glEnableVertexAttribArray(GLKVertexAttribPosition);

    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 12 * sizeof(GLfloat), 0);

    NSLog( @"******* 7" );

    [self.effect prepareToDraw];

    glDrawArrays(GL_TRIANGLES, 0, sizeof(parparit51OBJVertices) / sizeof(Vertex));

    NSLog( @"******* 7.5" );

    const GLfloat width = [glView drawableWidth];

    const GLfloat height = [glView drawableHeight];

    NSAssert(0 < width && 0 < height, @"Invalid drawble size");

    // Get info for picked location

    const GLKVector2 scaledProjectionPosition = {

        point.x / width,

        point.y / height

    };

    NSLog( @"******* 8" );

    GLubyte pixelColor[4];  // Red, Green, Blue, Alpha color

    GLint readLocationX = MIN((512 - 1),

                              (512 - 1) * scaledProjectionPosition.x);

    GLint readLocationY = MIN((512 - 1),

                              (512 - 1) * scaledProjectionPosition.y);

    glReadPixels(readLocationX,

                 readLocationY,

                 1,

                 1,

                 GL_RGBA,

                 GL_UNSIGNED_BYTE,

                 pixelColor);


    NSLog(@"pixelColor[0]=%i, pixelColor[1]=%i, pixelColor[2]=%i", pixelColor[0], pixelColor[1], pixelColor[2] );


    // Restore OpenGL state that pickTerrainEffect changed

    glBindFramebuffer(GL_FRAMEBUFFER, 0); // default frame buffer

    glViewport(0, 0, width, height); // full area of glView

    return 0;

}

-(void) buildFBO
{
    if ( 0 == _pickFBO )
    {

        GLuint colorTexture;

        // Create a texture object to apply to model

        glGenTextures(1, &colorTexture);

        glBindTexture(GL_TEXTURE_2D, colorTexture);

        // Set up filter and wrap modes for this texture object

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
                        GL_CLAMP_TO_EDGE);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
                        GL_CLAMP_TO_EDGE);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
                        GL_LINEAR);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                        GL_LINEAR_MIPMAP_LINEAR);

        // Allocate a texture image we can render into

        // Pass NULL for the data parameter since we don't need to

        // load image data. We will be generating the image by

        // rendering to this texture.

        glTexImage2D(GL_TEXTURE_2D,

                     0,

                     GL_RGBA,

                     512,

                     512,

                     0,

                     GL_RGBA,

                     GL_UNSIGNED_BYTE,

                     NULL);

        GLuint depthRenderbuffer;

        glGenRenderbuffers(1, &depthRenderbuffer);

        glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);

        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,

                              512, 512);

        glGenFramebuffers(1, &_pickFBO);

        glBindFramebuffer(GL_FRAMEBUFFER, _pickFBO);

        glFramebufferTexture2D(GL_FRAMEBUFFER,

                               GL_COLOR_ATTACHMENT0,

                               GL_TEXTURE_2D, colorTexture, 0);

        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);

        if(glCheckFramebufferStatus(GL_FRAMEBUFFER) !=

           GL_FRAMEBUFFER_COMPLETE)
        {
            NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));

            //+++tbd+++UtilityPickTerrainEffectDestroyFBO(fboName);

            return;
        }

        //#ifdef DEBUG
        //    {  // Report any errors
        GLenum error = glGetError();

        if(GL_NO_ERROR != error)
        {

            NSLog(@"GL Error: 0x%x", error);

        }
        //    }
        //#endif
    }
}

【问题讨论】:

    标签: ios opengl-es glkit picking ray-picking


    【解决方案1】:

    你的错误是你试图访问一个空指针——你几乎可以肯定传递一个你从未真正初始化过的数组给你的 GLKEffect 或通过 bufferData 调用将它提供给 OpenGL,因为错误发生在prepareToDraw 或 glDrawArrays。我可以看到有几种可能的解释,尽管我无法确认其中任何一个,因为相关信息是您如何分配您在 perpareToDraw 或 glDrawArrays 中使用的数据。

    首先是“parparit51OBJVertices”可能在堆上动态分配(你是在调用malloc之类的吗?你如何指定数组的大小),在这种情况下你对sizeof的调用将返回不正确的值( 0 我认为)这可能会导致 EXEC_BAD_ACESS。

    这似乎令人怀疑的另一部分是您正在调用 glDrawArrays 并传入顶点数量,但在此之前您为索引绑定了一个 VBO——充其量这是浪费的工作,因为 glDrawArrays 将忽略索引。更糟糕的是,如果您尝试绘制一个 OBJ,正如您的变量名称所暗示的那样,那么您可能根本没有绘制大量几何图形,因此即使您修复了 EXEC_BAD_ACCESS 问题,您仍然会得到不好的结果。

    【讨论】:

    • 感谢 Ben 的详细回答,但我相信这不是解决方案。 (vertices-3, colors-4, texturesUVs-2, normals-3) 的步幅数组和索引数组都是静态的,并在头文件中定义。当我使用 Gldrawarrays 在同一个项目的屏幕上显示它们时,两者都运行良好。
    • 我明白了。是的,那个 sizeof 调用显然不是你的问题,但我确信你的问题是你在某处给 OpenGL 一个空指针。鉴于它正在其他地方绘制(查看该代码以及所有这些东西的初始化位置会很有帮助),我的新理论是您调用 glBindVertexArrayOES(0) 来清除 VAO 正在清除一些数据然后你就忘记重新绑定了。
    • 我也怀疑你的 glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 12 * sizeof(GLfloat), 0 call。这实际上是 paraprit51OBJVertices 的大小吗?这在你的其他抽奖中有效吗?调用?另外,我的印象是 paraprit51OBJVertices 是 Vertex 类型(我认为是一个结构,给定你的 glDrawArrays 调用),但你给它的步幅为零。
    • 非常感谢您的帮助本。您愿意给我您的电子邮件,以便我可以将所有 xcode 项目发送给您,如果找到解决方案,我们将在此处发布它? (我的完整代码在屏幕上很好地呈现了 3d 对象,但是当我点击屏幕时它崩溃了)
    • 本,顺便说一句,据我所知,glvertexattribpointer 末尾的零指向步幅中的第一个位置,它并没有说步幅大小为零,因为我相信你在上面写过.此命令中的步幅大小由 12*sizeof(glfloat) 给出
    【解决方案2】:

    我遇到了 BAD_ACCESS 错误。 我通过删除处理索引的代码解决了这个问题:

    if(0 == _indexBufferID)
    {
      // Indices haven't been sent to GPU yet
      // Create an element array buffer for mesh indices
        glGenBuffers(1, &_indexBufferID);
        NSAssert(0 != _indexBufferID,
                 @"Failed to generate element array buffer");
    
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferID);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER,
                     sizeof( parparit51OBJIndices ),
                     parparit51OBJIndices,
                     GL_STATIC_DRAW);
    }
    else
    {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferID);
    }
    

    现在我仍然需要了解为什么上面更新的代码总是为任何选取的像素打印 0,0,0。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-10
      • 1970-01-01
      • 1970-01-01
      • 2015-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多