VAO и состояние буфера массива элементов

Недавно я писал код OpenGL 3.3 с Vertex Array Objects (VAO) и позже тестировал его на графическом адаптере Intel, где к своему разочарованию обнаружил, что привязка буфера массива элементов, очевидно, не является частью состояния VAO, поскольку вызов:

glBindVertexArray(my_vao);
glDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_INTEGER, 0);

не имел никакого эффекта, в то время как:

glBindVertexArray(my_vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, my_index_buffer); // ?
glDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_INTEGER, 0);

рендерил геометрию. Я думал, что это просто ошибка в реализации OpenGL от Intel (ведь в GL_ARB_vertex_array_object (и даже в GL_OES_vertex_array_object) четко указано, что массив элементов является частью сохраненного состояния), но затем это произошло на мобильной NVIDIA Quadro 4200. Это не весело.

Это ошибка драйвера, ошибка спецификации или ошибка где-то в моем коде? На GeForce 260 и 480 код работает безупречно.

У кого-нибудь был подобный опыт?

Что также странно, так это то, что GL_EXT_direct_state_access не имеет функции для привязки буфера массива элементов к VAO (но имеет функции для указания массивов вершинных атрибутов, а значит и буферов массивов). Производители GPU подделывают спецификации и обманывают нас, или что?

EDIT:

Изначально я не собирался показывать исходный код, поскольку считал, что в этом нет необходимости. Но, как и просили, вот минимальный тестовый пример, воспроизводящий проблему:

static GLuint n_vertex_buffer_object, p_index_buffer_object_list[3];
static GLuint p_vao[2];

bool InitGLObjects()
{
    const float p_quad_verts_colors[] = {
        1, 0, 0, -1, 1, 0,
        1, 0, 0, 1, 1, 0,
        1, 0, 0, 1, -1, 0,
        1, 0, 0, -1, -1, 0, // red quad
        0, 0, 1, -1, 1, 0,
        0, 0, 1, 1, 1, 0,
        0, 0, 1, 1, -1, 0,
        0, 0, 1, -1, -1, 0, // blue quad
        0, 0, 0, -1, 1, 0,
        0, 0, 0, 1, 1, 0,
        0, 0, 0, 1, -1, 0,
        0, 0, 0, -1, -1, 0 // black quad
    };
    const unsigned int p_quad_indices[][6] = {
        {0, 1, 2, 0, 2, 3},
        {4, 5, 6, 4, 6, 7},
        {8, 9, 10, 8, 10, 11}
    };
    glGenBuffers(1, &n_vertex_buffer_object);
    glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
    glBufferData(GL_ARRAY_BUFFER, sizeof(p_quad_verts_colors), p_quad_verts_colors, GL_STATIC_DRAW);
    glGenBuffers(3, p_index_buffer_object_list);
    for(int n = 0; n < 3; ++ n) {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[n]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(p_quad_indices[n]), p_quad_indices[n], GL_STATIC_DRAW);
    }

    glGenVertexArrays(2, p_vao);
    glBindVertexArray(p_vao[0]);
    {
        glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(0));
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(3 * sizeof(float)));
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[0]); // red
    }
    glBindVertexArray(0);

    glBindVertexArray(p_vao[1]);
    {
        glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(0));
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(3 * sizeof(float)));
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[1]); // blue
    }
    glBindVertexArray(0);

#ifdef BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[2]);
    // bind the buffer with the black quad (not inside VAO, should NOT be seen)
#endif // BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER

    // [compile shaders here]

    return true; // success
}

Приведенный выше код создает буфер вершин, содержащий три квада, красный, синий и черный. Затем создаются три индексных буфера, которые указывают на отдельные квады. Затем создаются и настраиваются два VAO, один из которых должен содержать индексы красного квада, а другой - индексы синего квада. Черный квадрик не должен рендериться вообще (предполагается, что BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER определен).

void onDraw()
{
    glClearColor(.5f, .5f, .5f, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    glDisable(GL_DEPTH_TEST);

    glUseProgram(n_program_object);

    static int n_last_color = -1;
    int n_color = (clock() / 2000) % 2;
    if(n_last_color != n_color) {
        printf("now drawing %s quad\n", (n_color)? "blue" : "red");
        n_last_color = n_color;
    }

    glBindVertexArray(p_vao[n_color]);
#ifdef VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[n_color]); // fixes the problem
#endif // VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}

Это очищает вьюпорт до серого и рендерит либо синий, либо красный квадрат повторяющимся образом (также выводится, какой именно). Хотя это работает на настольном GPU, это не работает на GPU ноутбука (черный квадрат отображается, если не определен макрос VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER. Неопределение макроса BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER делает квадрат синим, поскольку синий индексный буфер привязывается последним. Но это не приводит к рендерингу красного квада, несмотря ни на что.

Так что, как я понимаю, это либо фатальное заблуждение в моем понимании того, как должен работать VAO, либо ошибка в моем коде, либо ошибка драйвера.

Полный исходный текст
Бинарные файлы (windows, 32 bit)

22
задан the swine 22 September 2014 в 15:57
поделиться

1 ответ

Я могу представить, что буфер ELEMENT не кэшируется; если вы сделаете:

glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(0));

Это все равно что сказать:

gBoundBuffer_GL_ARRAY_BUFFER=n_vertex_buffer_object;
currentVAO->enable|=(1<<0);
currentVAO->vertexBuffer=IndexToPointer(gBoundBuffer_GL_ARRAY_BUFFER);

Другими словами, glBindBuffer() ничего не делает, но устанавливает значение GL_ARRAY_BUFFER. Это в glVertexAttribPointer(), где вы модифицируете VAO.

Итак, когда вы делаете:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[0]);
glBindVertexArray(0);

Вы действительно делаете:

gBoundBuffer_GL_ELEMENT_ARRAY_BUFFER=p_index_buffer_object_list[0];
currentVAO=0;

Где имеет смысл, что привязка GL_ELEMENT_ARRAY_BUFFER ничего не делает. Я не уверен, что существует glVertexAttribPointer() -подобный вариант для элементов (например, glElementPointer()), который фактически будет действовать на VAO.

-1
ответ дан 29 November 2019 в 04:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: