выбор цвета glkit в opengl iOS

Речь идет о выборе 3D-объекта с помощью выбора цвета. Я рисую свою сетку (обновление: которая находится в статическом заголовочном файле и отлично отображается на экране в том же проекте) в новый закадровый буфер кадра, а затем я использую glReadPixels, чтобы определить, коснулся ли ее пользователь.

Следуя коду из проекта opengles_ch10_1 из книги Эрика М. Бака «Изучение OpenGLES для iOS», я написал следующий код, который всегда выводит в 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
    }
}

person user2492853    schedule 28.09.2013    source источник


Ответы (2)


Ваша ошибка заключается в том, что вы пытаетесь получить доступ к нулевому указателю - вы почти наверняка передаете массив, который вы никогда не инициализировали, в свой GLKEffect или передаете его OpenGL с вызовом bufferData, учитывая, что ошибка возникает внутри prepareToDraw или glDrawArrays . Есть несколько возможных объяснений этого, которые я вижу, хотя я не могу подтвердить ни одно из них, поскольку важная информация заключается в том, как вы распределяете данные, которые вы используете либо в perpareToDraw, либо в glDrawArrays.

Во-первых, "parparit51OBJVertices" может динамически выделяться в куче (вы вызываете malloc или что-то подобное? Как вы указываете размер массива), и в этом случае ваши вызовы sizeof будут возвращать неверные значения (я думаю, 0 ), что может привести к ошибке EXEC_BAD_ACESS.

Другая часть этого, которая кажется подозрительной, заключается в том, что вы вызываете glDrawArrays и передаете количество вершин, но перед этим вы связываете VBO для индексов - в лучшем случае это напрасная работа, поскольку glDrawArrays будет игнорировать индексы. Хуже того, если вы пытаетесь нарисовать OBJ, как следует из названия вашей переменной, то, вероятно, вы вообще не рисуете значительную часть геометрии, поэтому, даже если вы устраните проблему с EXEC_BAD_ACCESS, вы все равно получите плохие результаты.

person Ben Pious    schedule 29.09.2013
comment
Спасибо Бен за ваш подробный ответ, но я считаю, что это не решение. Как массив шагов (вершины-3, цвета-4, текстурыUVs-2, нормали-3), так и массив индексов являются статическими и определены в заголовочном файле. Оба работают хорошо, когда я отображаю их на экране в одном проекте, используя Gldrawarrays. - person user2492853; 29.09.2013
comment
Я понимаю. Да, этот вызов sizeof явно не ваша проблема, но я уверен, что ваша проблема в том, что вы даете OpenGL нулевой указатель где-то вдоль строки. Учитывая, что он рисует где-то еще (было бы полезно увидеть этот код, а также то, где все эти вещи инициализируются), моя новая теория будет заключаться в том, что ваш вызов glBindVertexArrayOES(0) для очистки VAO очищает некоторые данные, которые затем вы забываете перепривязать. - person Ben Pious; 29.09.2013
comment
Я также с подозрением отношусь к вашему вызову glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 12 * sizeof(GLfloat), 0. Действительно ли это размер paraprit51OBJVertices? Это работает в вашем другом вызове отрисовки? Кроме того, это было мое впечатление что paraprit51OBJVertices имеет тип Vertex (я полагаю, это структура, учитывая ваш вызов glDrawArrays), но вы даете ей нулевой шаг. - person Ben Pious; 29.09.2013
comment
большое спасибо за вашу помощь Бен. Не могли бы вы дать мне свой адрес электронной почты, чтобы я мог отправить вам весь проект xcode, и если решение будет найдено, мы опубликуем его здесь? (мой полный код хорошо отображает 3D-объекты на экране, но когда я нажимаю на экран, он вылетает) - person user2492853; 30.09.2013
comment
Бен, кстати, насколько я знаю, ноль в конце glvertexattribpointer указывает на первое место в шаге, и это не говорит, что размер шага равен нулю, как я полагаю, вы написали выше. размер шага в этой команде определяется как 12*sizeof(glfloat) - person user2492853; 30.09.2013
comment
На самом деле я говорил о 12 * sizeof(GLFloat), но на самом деле это не должно быть вашей проблемой... Я неправильно запомнил параметры для glVertexAttribPointer, подумал, что этот предполагаемый параметр был размером всего массива, хотя на самом деле это шаг, и поэтому было странно, что это не sizeof(paraparit15OBJ), и что последним параметром был шаг, а не смещение... Не обращайте внимания, это был полный отвлекающий маневр. Извините, что потратил ваше время. - person Ben Pious; 30.09.2013
comment
Бен, спасибо за помощь. мы все делаем ошибки, если вы все еще хотите помочь, пожалуйста, дайте мне свой адрес электронной почты. - person user2492853; 30.09.2013

У меня была ошибка 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 для любого выбранного пикселя.

person user2492853    schedule 30.09.2013