OpenGL: возможно ли использовать VAO без указания VBO?

12

Во всех руководствах, которые я могу найти о VAO (объектах Vertex Array), они показывают, как их использовать, настраивая атрибуты вершин и связывая VBO (объект буфера вершин). Но я хочу создать VAO, который будет настроен для набора VBO в сочетании с фиксированным шейдером, где каждый буфер использует один и тот же шаблон данных (вершина, uv, цвет и т. Д.). Итак, я хочу создать один VAO для нескольких VBO, которые будут рисоваться с использованием одного шейдера.

Я не смог найти никакой демонстрации по этому поводу, поэтому я решил просто попробовать. Но это не работает и вылетает при glDrawArrayвызове. Похоже, VBO не связан. Вот код, который я использую:

Оказание:

/* Prepare vertex attributes */
glBindVertexArrayOES(vao);

/* Upload the buffer to the GPU */
glBindBuffer(GL_ARRAY_BUFFER, pool->next());
glBufferSubData(GL_ARRAY_BUFFER, 0, parts * buffer.stride() * 6, buffer.getBuffer());

/* Draw the triangles */
glDrawArrays(GL_TRIANGLES, 0, parts * 6);

glBindVertexArrayOES(0);

VAO Создание:

glBindVertexArrayOES(vao);

glEnableVertexAttribArray(ls.position);
glVertexAttribPointer(ls.position, 2, GL_FLOAT, GL_FALSE, buffer.stride(), 0);

glEnableVertexAttribArray(ls.color);
glVertexAttribPointer(ls.color, 3, GL_FLOAT, GL_FALSE, buffer.stride(), GL_BUFFER_OFFSET(buffer.dataTypeSize * 2));

glBindVertexArrayOES(0);

Где lsэто простой, structкоторый содержит атрибуты местоположения.

В части «Рендеринг» обмен glBindBufferи glBindVertexArrayOESокружение тоже не работали.

Итак, вопрос в том, возможно ли это сделать, или мне придется создавать для каждого буфера VAO? И если мне нужно создать VAO для каждого VBO, возможно ли обновить данные VBO, используя glBufferSubDataв сочетании с VAO?

Мартейн Курто
источник

Ответы:

18

VAO не содержат состояние "glBindBuffer", за исключением состояния GL_ELEMENT_ARRAY_BUFFERпривязки. То, что вы не понимаете, это то, что glBindBuffer(GL_ARRAY_BUFFER) ничего не делает . Ну, это не делает ничего, что касается рендеринга. Попытайся; прямо перед вызовом glDraw *, call glBindBuffer(GL_ARRAY_BUFFER, 0); Ваш рендеринг будет работать нормально.

Способ glVertexAttribPointerработает так: он смотрит на то, что в данный момент glVertexAttribPointerвызывается GL_ARRAY_BUFFER . Не во время рендера. Не позже. Прямо в этот момент. Он берет любой объект буфера, который был там, и сохраняет его в другом фрагменте состояния OpenGL, который инкапсулирован в VAO.

В общем, у вас есть два варианта, один из которых является новым и, вероятно, не должен использоваться в данный момент.

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

Когда приходит время рендеринга, вы можете рендерить часть вершин. glDrawArraysпринимает ряд элементов для рендеринга, и вы можете настроить свои индексы, glDrawElementsчтобы сделать то же самое. Кроме того, вы можете использовать glDrawElementsBaseVertexдля смещения вершин , так что смещение добавляется к каждому индексу. Это смещение - количество вершин перед этой вершиной в большом массиве.

Другой альтернативой является использование относительно новой системы разделения атрибутов формата, добавленной в GL 4.3. Это позволит вам изменить буферы без сброса формата. Таким образом, вы бы связали один VAO, а затем просто заменили его в разных буферах по мере необходимости glBindVertexBuffer. Это также доступно в ES 3.1.

Николь Болас
источник
1
Драйверы NV GL4.3 теперь полностью выпущены.
Максимус Минимус
Большое спасибо за объяснение того, как работает glVertexAttribPointer. Это было поучительно и объясняет, почему и как это работает.
Мартин Курто
5

VAO хранит состояние glVertexAttribPointer. Изменение VAO не влияет на текущий glBindBuffer, а изменение glBindBuffer не влияет на VAO. Только вызов glVertexAttribPointer влияет на VAO, записывая используемый буфер при вызове.

Так что ответ на ваш вопрос - нет.

Один из вариантов, если вы хотите уменьшить количество объектов, - это поместить все данные сетки в один большой VBO и указать, где данные сетки находятся в VBO, в вызове glDrawArrays, используя аргументы first и count.

ccxvii
источник
Все статьи в интернете о VAO показывают, что привязка VAO автоматически связывает VBO. Итак, я думаю, что VAO - это конфигурация, которая содержит как состояния glVertexAttribPointer, так и состояние glBindBuffer . И да, связывание VBO, когда VAO связан, изменяет VAO. Я говорю это, потому что я проверил это, и это эффективно делает. Откуда эта информация?
Мартин Курто
2
@MartijnCourteaux: « Все статьи в интернете о VAO показывают, что привязка VAO автоматически связывает VBO. » Нет, они этого не делают. OpenGL Wiki по спецификации вершин этого не делает. Действительно, покажите мне одну статью в интернете, в которой говорится, что любое glBindBufferсостояние, кроме GL_ELEMENT_ARRAY_BUFFER измененного, связывается с VAO.
Николь Болас
Другой ответ хорошо объяснил, как работает метод glVertexAttribPointer. Он использует VBO, связанный в то время. Вы правы! Спасибо, я понимаю, прямо сейчас.
Мартин Курто