Недавно я писал некоторый код 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 бит)