Проблемы с отрисовкой объектов OpenGL из VBO с использованием текстур, нормалей и списка индексов

Я надеюсь, что это не кажется слишком большим дампом кода, но я действительно понятия не имею, почему это не работает. Я пытался получить ошибки от glGetError(), и, похоже, он всегда возвращает 0. Я пытался включить только тот код, который, по моему мнению, влияет на проблему, поскольку другой код работал нормально в большинстве других ситуаций, в которых я его использовал.

В любом случае сначала код:

Это мой основной цикл рендеринга:

float rotate = 0.0f;
void Render(SDL_Window *window)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glLoadIdentity();

    gluLookAt(-2,-2,-10,   0,0,0,   0,1,0);

    glRotatef(rotate, 0, 1, 0);
    // Start drawing
    glUseProgramObjectARB( *shader->GetShaderProgram() );
    texture->EnableTexture( *shader->GetShaderProgram(),"tex" );
    cube->Render();
    SDL_GL_SwapWindow(window);
    rotate = rotate+1;
    glUseProgramObjectARB(0);
}

Это мои объекты рендеринга (куб-> Render()):

void Object3DVBO::Render()
{

    //Bind the buffers and tell OpenGL to use the Vertex Buffer Objects (VBO's), which we already prepared earlier
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboID); 
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, texcoordsID);
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, normalID);
    glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indiceID);

    //Enable states, and render (as if using vertex arrays directly)
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);

    glTexCoordPointer(2, GL_FLOAT, 0, 0);
    glNormalPointer(GL_FLOAT, 0, 0);
    glVertexPointer(3, GL_FLOAT, 0,  0);

    if(textureid > 0) {
        glEnable(GL_TEXTURE_2D);      // Turn on Texturing
        //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
        glBindTexture(GL_TEXTURE_2D, textureid);
    }

    //Draw the thing!
    glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_BYTE, 0);
    //restore the GL state back
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
    if(textureid > 0) {
        glDisable(GL_TEXTURE_2D);      // Turn off Texturing
        glBindTexture(GL_TEXTURE_2D, textureid);
    }

    glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); //Restore non VBO mode
    glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
};

Это код для инициализации VBO:

void Object3DVBO::SetVBO()
{
    // Vertices:
    glGenBuffersARB(1, &vboID);
    glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboID);
    glBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof(GLfloat)*vertices.size(), &vertices, GL_STATIC_DRAW_ARB );

    // Vertices:
    glGenBuffersARB(1, &indiceID);
    glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, indiceID);
    glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(GLubyte)*indices.size(), &indices, GL_STATIC_DRAW_ARB );

    // Normals:
    glGenBuffersARB(1, &normalID);
    glBindBufferARB( GL_ARRAY_BUFFER_ARB, normalID);
    glBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof(GLfloat)*normals.size(), &normals, GL_STATIC_DRAW_ARB );

    // Texture coordinates:
    glGenBuffersARB(1, &texcoordsID);
    glBindBufferARB( GL_ARRAY_BUFFER_ARB, texcoordsID);
    glBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof(GLfloat)*texCoords.size(), &texCoords, GL_STATIC_DRAW_ARB );

    vertices.clear();
    normals.clear();
    texCoords.clear();
    indices.clear();
}

и, наконец, самая простая модель, которую я сделал в текстовом файле:

mesh:
-0.5 -0.5 0.0
0.5 -0.5 0.0
0.5 0.5 0.0
-0.5 0.5 0.0
normals:
0 0 -1
0 0 -1
0 0 -1
0 0 -1
texcoords:
0.0 0
1.0 0
1.0 1.0
0.0 1.0
indices:
0 0 0 1 1 1 2 2 2 0 0 0 2 2 2 3 3 3
end:

Что вне этого, так это чтение текстового файла и сохранение его в векторах из функций. Вот заголовочный файл объекта:

class Object3DVBO
{
public:
    Object3DVBO(const char* objectfilename, GLuint textureid);
    ~Object3DVBO();
    void Render();
private:
    GLuint  vboID, 
            texcoordsID, 
            normalID, 
            textureid,
            indiceID;
    int     vertCount,
            indexCount;
    std::vector<GLfloat> vertices;
    std::vector<GLfloat> normals;
    std::vector<GLfloat> texCoords;
    std::vector<GLubyte> indices;

    void ConvertToReadable( std::vector<GLfloat> v, std::vector<GLfloat> n, std::vector<GLfloat> tc, std::vector<GLint> i);
    void ReadObjectData( const char* objectfilename);
    void SetVBO();
};

Другие части в основном делают то же самое для пары шейдеров и текстуры, они, похоже, работают нормально (на данный момент шейдер только версии 120, поэтому он использует ftransform() и т. д. и т. д.)

У меня проблема в том, что когда я использую этот код и glDrawElements с использованием индексов, я получаю черный экран (нигде нет ошибок), и если я затем переключаюсь на glDrawArrays, он работает (или, по крайней мере, я вижу ЧТО-ТО. Я прочитал много руководств, примеры и другие сообщения SO, пытающиеся найти, что я делаю неправильно, но ни одно из решений/учебников до сих пор не имело никакого значения.

Я делаю это в образовательных целях, поэтому мне действительно нужно использовать glDrawElements и индексы. Любая помощь будет принята с благодарностью!

Пс. Если кого-то интересует версия SDL, то это SDL2.


person OMK    schedule 06.11.2013    source источник
comment
Если вы хотите, чтобы это работало переносимо на чем-либо OpenGL 1.4 или новее, не используйте суффикс ARB в вызовах API вашего объекта буфера. OS X, например, предоставляет все эти функции, но не древнее расширение ARB, от которого они произошли. glBindBufferARB (...) выдаст ошибку компоновщика на этой платформе. То же самое касается программ GLSL: используйте расширение ядра, а не расширение ARB (glUseProgramObjectARB (...) равно glUseProgram (...) в ядре OpenGL 2.0+). Если вы следуете учебнику, я настоятельно рекомендую использовать что-то более новое.   -  person Andon M. Coleman    schedule 06.11.2013
comment
Я использую материал ARB, поэтому я могу использовать самую старую версию GLSL (чтобы я мог использовать ftransform()). Я думал, что вам нужны функции ARB для этого. Я что-то неправильно понял?   -  person OMK    schedule 06.11.2013
comment
Да, языковая версия GLSL и фактическое расширение API — это две разные вещи. Вы можете использовать ftransform (...) в GLSL 440, если у вас есть профиль совместимости. Я имел в виду, что существуют реализации, которые поддерживают только базовую версию GLSL и Vertex Buffer Objects (например, OS X). Вероятность того, что вы собираетесь запускать свое программное обеспечение на графическом процессоре старше 1.4 (2003 г.), ничтожно мала, но вероятность того, что вы когда-нибудь захотите запустить его на OS X, довольно высока. Если вы установите OpenGL 2.0 в качестве основы, у вас будет меньше проблем.   -  person Andon M. Coleman    schedule 07.11.2013


Ответы (1)


Вот суть вашей проблемы:

//Bind the buffers and tell OpenGL to use the Vertex Buffer Objects (VBO's), which we already prepared earlier
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboID);          // 1
glBindBufferARB(GL_ARRAY_BUFFER_ARB, texcoordsID);    // 2
glBindBufferARB(GL_ARRAY_BUFFER_ARB, normalID);       // 3
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indiceID);

//Enable states, and render (as if using vertex arrays directly)
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);

glTexCoordPointer(2, GL_FLOAT, 0, 0);                 // 2
glNormalPointer(GL_FLOAT, 0, 0);                      // 3
glVertexPointer(3, GL_FLOAT, 0,  0);                  // 1

Строки, обозначенные 1, 2 и 3, должны идти парами. То есть, поскольку вы можете одновременно иметь только одну привязку VBO, и она предоставляет контекст для вызова, например, glTexCoordPointer (...), вам нужно установить указатели, пока привязан соответствующий VBO.

Вы можете исправить это следующим образом:

//Bind the buffers and tell OpenGL to use the Vertex Buffer Objects (VBO's), which we already prepared earlier
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboID);          // 1
glVertexPointer(3, GL_FLOAT, 0,  0);                  // 1

glBindBufferARB(GL_ARRAY_BUFFER_ARB, texcoordsID);    // 2
glTexCoordPointer(2, GL_FLOAT, 0, 0);                 // 2

glBindBufferARB(GL_ARRAY_BUFFER_ARB, normalID);       // 3
glNormalPointer(GL_FLOAT, 0, 0);                      // 3


glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indiceID);

//Enable states, and render (as if using vertex arrays directly)
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);

У вас есть еще одна проблема, которая на самом деле не столько ошибка, сколько проблема производительности. GL_UNSIGNED_BYTE не является аппаратно поддерживаемым типом вершинного индекса. Драйвер должен преобразовать ваш массив индексов в 16-битный тип, чтобы аппаратное обеспечение могло его использовать, поэтому нет никакой реальной выгоды от использования 8-битных индексов, когда вы собираетесь хранить их в VBO. Большинство профилировщиков и драйверов OpenGL с включенным выводом отладки будут генерировать предупреждение о производительности, если вы попытаетесь сделать это.

person Andon M. Coleman    schedule 06.11.2013
comment
Привет, спасибо за ответ. К сожалению, я получаю ту же ошибку (точнее: нет ошибки, просто черный экран...) :( - person OMK; 06.11.2013
comment
Быстро проверил gDebugger и, по крайней мере, не нашел ничего очевидного. - person OMK; 06.11.2013