Лучшие практики OpenGL VAO

79

Я столкнулся с проблемой, которая, по моему мнению, зависит от VAO, но я не уверен ..

Я не уверен в правильном использовании VAO, то, что я делал во время инициализации GL, было простым

glGenVertexArrays(1,&vao)

за которым следует

glBindVertexArray(vao)

а позже, в моем конвейере рисования, я просто вызвал glBindBuffer (), glVertexAttribPointer (), glEnableVertexAttribArray () и т. д., не заботясь о изначально привязанном VAO

это правильная практика?

user815129
источник
31
Я не уверен, почему этот вопрос получил -1 .. он не показывает никаких усилий и. Или неясен? Я потратил 3 дня, борясь со спецификациями, вики и форумами, прежде чем опубликовать это, и не получил никакой реальной причины, по которой я должен использовать VAO .. meh ..
user815129
18
Потому что для крутых братанов все очевидно, как только у них под рукой есть вырезки и вставки, зачем вообще разбираться в вещах ??
mlvljr 02

Ответы:

94

VAO действуют аналогично VBO и текстурам в отношении того, как они связаны. Наличие одной привязки VAO для всей длины вашей программы не принесет никакого выигрыша в производительности, потому что вы можете вообще просто выполнять рендеринг без VAO. Фактически, это может быть медленнее в зависимости от того, как реализация перехватывает настройки атрибутов вершин во время их рисования.

Смысл VAO состоит в том, чтобы запустить все методы, необходимые для рисования объекта один раз во время инициализации, и исключить все дополнительные накладные расходы на вызов методов во время основного цикла. Дело в том, чтобы иметь несколько VAO и переключаться между ними при рисовании.

С точки зрения передовой практики, вот как вам следует организовать свой код:

initialization:
    for each batch
        generate, store, and bind a VAO
        bind all the buffers needed for a draw call
        unbind the VAO

main loop/whenever you render:
    for each batch
        bind VAO
        glDrawArrays(...); or glDrawElements(...); etc.
    unbind VAO

Это позволяет избежать беспорядка, связанного с связыванием / отвязкой буферов и передачей всех настроек для каждого атрибута вершины, и заменяет его одним вызовом метода, связывающим VAO.

Роберт Рухани
источник
1
неужели надо ВАО отвязать? Я нашел разные практики
user815129
3
В этом нет необходимости, но я обычно делаю это в случае, если какой-то другой объект решает выполнить рендеринг без VAO (отладочная библиотека рисования / рендеринга шрифтов и т. Д.), Поскольку это (насколько мне известно) заменит настройки атрибутов для привязанного в данный момент VAO .
Роберт Рухани
2
Почему отвязка происходит только один раз и вне цикла? Разве отвязка не должна происходить для каждой партии?
batbrat
12
Привязка нового VAO заменяет старый VAO. Отвязка внутри цикла была бы лишней работой.
Роберт Рухани
1
@RobertRouhani Тогда есть ли польза от последней отмены привязки VAO? (Ни на что не влияет развязывание?)
Матин Улхак
27

Нет, это не то, как вы используете VAO. Вы должны использовать VAO так же, как вы используете VBO, текстуры или шейдеры. Сначала настройте его. А во время рендеринга только их привязывайте, не изменяя.

Итак, с VAO вы делаете следующее:

См. Также эти ссылки:

Мартиньш Можейко
источник
1
Этот ответ должен быть выше. Он специально исправил мою трудную для отслеживания ошибку.
abarax
3
glEnableVertexAttribArray(...)следует называть раньше glVertexAttribPointer(...). Некоторые водители (включая меня) на самом деле не нравится, наоборот.
RecursiveExceptionException
2
Проголосовали за, потому что в большинстве примеров не ясно, что вы ДОЛЖНЫ вызывать glEnableVertexAttribArray во время привязки VAO.
Руперт Роунсли,
10

это правильная практика?

Да, это совершенно законно и действительно. Это хорошо? Хорошо...

По этому поводу проводилось неформальное тестирование производительности . И кажется, по крайней мере, на оборудовании NVIDIA, где это тестировалось, «правильное» использование VAO (то есть то, что отстаивали все остальные) на самом деле происходит медленнее. во многих случаях . Это особенно верно, если изменение VAO не влияет на то, какие буферы связаны.

Насколько мне известно, подобное тестирование производительности на оборудовании AMD не проводилось. В общем, если с ними что-то не изменится, это приемлемое использование VAO.

Никол Болас
источник
2
Конечно, если у каждого VAO будет достаточно различных состояний, которые он отслеживает, переключение буферов и атрибутов атрибутов вручную во время цикла рендеринга потребует большого количества вызовов, мы бы увидели улучшение производительности от использования VAO?
Стивен Лу
К сожалению, ссылка мертвая.
Ник Каплингер
3

Ответ Роберта выше работал у меня, когда я его пробовал. Для чего здесь стоит код на Go использования нескольких объектов атрибутов вершин:

// VAO 1

vao1 := gl.GenVertexArray()
vao1.Bind()

vbo1 := gl.GenBuffer()
vbo1.Bind(gl.ARRAY_BUFFER)

verticies1 := []float32{0, 0, 0, 0, 1, 0, 1, 1, 0}
gl.BufferData(gl.ARRAY_BUFFER, len(verticies1)*4, verticies1, gl.STATIC_DRAW)

pa1 := program.GetAttribLocation("position")
pa1.AttribPointer(3, gl.FLOAT, false, 0, nil)
pa1.EnableArray()
defer pa1.DisableArray()

vao1.Unbind()

// VAO 2

vao2 := gl.GenVertexArray()
vao2.Bind()

vbo2 := gl.GenBuffer()
vbo2.Bind(gl.ARRAY_BUFFER)

verticies2 := []float32{-1, -1, 0, -1, 0, 0, 0, 0, 0}
gl.BufferData(gl.ARRAY_BUFFER, len(verticies2)*4, verticies2, gl.STATIC_DRAW)

pa2 := program.GetAttribLocation("position")
pa2.AttribPointer(3, gl.FLOAT, false, 0, nil)
pa2.EnableArray()
defer pa2.DisableArray()

vao2.Unbind()

Затем в основном цикле вы можете использовать их как таковые:

for !window.ShouldClose() {
    gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

    vao1.Bind()
    gl.DrawArrays(gl.TRIANGLES, 0, 3)
    vao1.Unbind()

    vao2.Bind()
    gl.DrawArrays(gl.TRIANGLES, 0, 3)
    vao2.Unbind()

    window.SwapBuffers()
    glfw.PollEvents()

    if window.GetKey(glfw.KeyEscape) == glfw.Press {
        window.SetShouldClose(true)
    }
}

Если вы хотите увидеть полный исходный код, он доступен в виде Gist на основе примеров в go-gl:

https://gist.github.com/mdmarek/0f73890ae2547cdba3a7

Спасибо всем за оригинальные ответы, у меня был тот же вопрос, что и у ECrownofFire.

Марек
источник
3
Вам не нужно отвязать vao1 перед привязкой vao2, достаточно просто привязать vao2.
havokentity