VAO и состояние буфера массива элементов - PullRequest
22 голосов
/ 23 января 2012

Недавно я писал некоторый код OpenGL 3.3 для объектов Vertex Array (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);

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

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

Кто-нибудь имел подобный опыт?

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

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

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

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

Полный источник
Двоичные файлы (Windows, 32 бит)

Ответы [ 4 ]

25 голосов
/ 29 июня 2012

Через некоторое время я обнаружил, что это на самом деле было моим видом плохого. Ноутбук с мобильной видеокартой NVIDIA Quadro 4200 был настроен таким образом, чтобы все приложения по умолчанию работали на графике Intel, даже когда ноутбук находился в режиме производительности. Я не понимаю, почему кто-то хотел бы сделать это, поскольку тогда ни у одного приложения не было возможности использовать более мощный графический процессор из OpenGL (все еще можно было использовать его для OpenCL, поскольку существует явный выбор устройства, также возможно для DirectX - это объясняет, почему некоторые игры работали без сбоев.

Тем не менее, описанное ошибочное поведение - это просто ошибка в драйверах Intel, вот и все. Драйверы Intel не сохраняют ELEMENT_ARRAY_BUFFER_BINDING. Там.

Я искренне извиняюсь за то, что задал вопрос, поскольку не было никакого способа дать хороший ответ, не зная выше.

17 голосов
/ 06 февраля 2012

Я действительно считаю, что ARB VAO отсутствует состояние привязки буфера массива элементов (или любой другой привязки буфера).

Вера не требуется;спецификация говорит факты.

Из спецификации ARB_vertex_array_object :

Команда

void GenVertexArrays(sizei n, uint *arrays);

возвращаетпредыдущие неиспользуемые имена объектов массива вершин в.Эти имена помечены как используемые, только для целей GenVertexArrays, и инициализируются с помощью состояния, указанного в таблицах 6.6 (за исключением состояния селектора CLIENT_ACTIVE_TEXTURE), 6.7 и 6.8 (за исключением состояния ARRAY_BUFFER_BINDING).

Итак, у нас это есть: все состояния, охватываемые VAO, являются содержимым этих трех таблиц, с отмеченными исключениями.

Расширение написано для Спецификация графики OpenGL версии 2.1 (PDF) .Поэтому любые номера страниц, метки разделов или номера таблиц имеют ссылки относительно этой спецификации.

Я не собираюсь копировать эти три таблицы здесь.Но если вы посмотрите на страницу 273 (по количеству страниц спецификации) / страницу 287 (по количеству физических страниц), вы найдете таблицу 6.8.И на этой таблице следующее:

  • ELEMENT_ARRAY_BUFFER_BINDING

Здесь нет двусмысленности.Информация не может быть четко изложена.Но это там , несомненно.ELEMENT_ARRAY_BUFFER_BINDING является частью состояния VAO.

Следовательно, ваша проблема может исходить из одного из двух источников:

  1. Ошибка драйвера.Как я сказал в комментарии, ошибка драйвера кажется маловероятной.Не невозможно, просто вряд ли.Драйверы NVIDIA довольно похожи для различного оборудования, а VAO едва ли отражаются в оборудовании.Если вы не используете разные версии драйверов, нет оснований ожидать, что ошибка будет вызвана ошибкой драйвера.

  2. Ошибка пользователя.Я знаю, что вы утверждаете, что ваш код работает, и поэтому он в порядке. Каждый заявляет о каком-то коде.И было много раз, когда я клялся, что какой-то код работал просто отлично.Все же это было сломано;Просто так получилось.Такое случается.Если вы разместите свой код, то, по крайней мере, мы сможем обесценить эту возможность.В противном случае у нас нет ничего, кроме вашего слова.И учитывая, как часто люди ошибаются в этом, это не стоит много.

0 голосов
/ 23 сентября 2013

Я могу представить себе, что буфер 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.

0 голосов
/ 27 июня 2012

Вероятной причиной этого является то, что ваш адаптер Intel не может предоставить контекст OpenGL 3.3, но вместо этого по умолчанию используется 2.1 или подобное. Как указывали другие, в более ранних версиях OpenGL состояние буфера массива элементов не было частью VAO.

...