Najlepsze praktyki OpenGL VAO

79

Stoję przed problemem, który moim zdaniem jest zależny od VAO, ale nie jestem pewien ...

Nie jestem pewien co do poprawnego użycia VAO, to co robiłem podczas inicjalizacji GL było proste

glGenVertexArrays(1,&vao)

po którym następuje

glBindVertexArray(vao)

a później, w moim potoku rysunkowym, właśnie wywołałem glBindBuffer (), glVertexAttribPointer (), glEnableVertexAttribArray () i tak dalej ... bez dbania o początkowo powiązany VAO

czy to poprawna praktyka?

user815129
źródło
31
Nie jestem pewien, dlaczego to pytanie ma -1 .. nie pokazuje żadnego wysiłku i. Lub jest niejasne? Spędziłem 3 dni zmagając się ze specyfikacjami, wiki i forami przed opublikowaniem tego i nie znalazłem żadnego prawdziwego powodu, dla którego powinienem używać VAO…
eee
18
Ponieważ dla fajnych braci wszystko jest oczywiste, gdy tylko mają pod ręką swoje kawałki i pasty, po co zawracać sobie głowę zrozumieniem rzeczy?
mlvljr

Odpowiedzi:

94

VAO działają podobnie do VBO i tekstur pod względem sposobu ich wiązania. Posiadanie jednego VAO związanego na całą długość programu nie przyniesie żadnych korzyści w zakresie wydajności, ponieważ równie dobrze możesz po prostu renderować bez VAO. W rzeczywistości może to być wolniejsze w zależności od tego, jak implementacja przechwytuje ustawienia atrybutów wierzchołków podczas ich rysowania.

Celem VAO jest uruchomienie wszystkich metod niezbędnych do narysowania obiektu raz podczas inicjalizacji i wyeliminowanie całego dodatkowego obciążenia wywołania metody podczas głównej pętli. Chodzi o to, aby mieć wiele VAO i przełączać się między nimi podczas rysowania.

Jeśli chodzi o najlepsze praktyki, oto jak powinieneś zorganizować swój kod:

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

Pozwala to uniknąć bałaganu związanego z wiązaniem / odłączaniem buforów i przekazywaniem wszystkich ustawień dla każdego atrybutu wierzchołka i zastępuje je jednym wywołaniem metody, wiążąc VAO.

Robert Rouhani
źródło
1
czy naprawdę konieczne jest rozwiązanie VAO? Znalazłem różne praktyki
user815129
3
Nie jest to konieczne, ale zwykle robię to na wypadek, gdyby jakiś inny obiekt zdecydował się renderować bez VAO (debugowanie rysunku / biblioteki renderowania czcionek / itp.), Ponieważ to (o ile wiem) zastąpiłoby ustawienia atrybutów dla aktualnie powiązanego VAO .
Robert Rouhani,
2
Dlaczego rozłączenie występuje tylko raz i poza pętlą? Czy odwiązanie nie powinno odbywać się na podstawie partii?
batbrat
12
Powiązanie nowego VAO zastępuje stare VAO. Rozpinanie wewnątrz pętli byłoby po prostu dodatkową pracą.
Robert Rouhani
1
@RobertRouhani Czy w takim razie jest jakaś korzyść z ostatniego rozwiązanego VAO? (Czy rozpinanie niczego nie wpływa?)
Mateen Ulhaq,
27

Nie, nie w ten sposób używasz VAO. Powinieneś używać VAO w ten sam sposób, w jaki używasz VBO, tekstur lub shaderów. Najpierw skonfiguruj. A podczas renderowania tylko Bind je, bez modyfikowania.

Więc z VAO wykonujesz następujące czynności:

Zobacz także te linki:

Mārtiņš Možeiko
źródło
1
Ta odpowiedź powinna być wyższa. Naprawiono mój trudny do wyśledzenia błąd.
abarax
3
glEnableVertexAttribArray(...)należy zadzwonić wcześniej glVertexAttribPointer(...). Niektórzy kierowcy (w tym mój) naprawdę nie lubią tego na odwrót.
RecursiveExceptionException
2
Zaopiniowano, ponieważ większość przykładów nie jest jasnych, że MUSISZ wywołać funkcję glEnableVertexAttribArray podczas wiązania VAO.
Rupert Rawnsley
10

czy to poprawna praktyka?

Tak, jest to całkowicie legalne i ważne. Czy to jest dobre? Dobrze...

Były pewne nieformalne testy wydajności tego rodzaju rzeczy. I wydaje się, że przynajmniej na sprzęcie NVIDIA, na którym to testowano, „właściwe” użycie VAO (tj. Za tym, co wszyscy zalecali) w wielu przypadkach jest wolniejsze . Jest to szczególnie prawdziwe, jeśli zmiana VAO nie zmienia, które bufory są powiązane.

O ile mi wiadomo, żadne podobne testy wydajności nie zostały przeprowadzone na sprzęcie AMD. Ogólnie rzecz biorąc, jeśli coś się z nimi nie zmieni, jest to dopuszczalne użycie VAO.

Nicol Bolas
źródło
2
Z pewnością, gdy każdy VAO ma wystarczająco dużo różnych stanów, które śledzi, że ręczne przełączanie buforów i atrybutów ptr podczas pętli renderowania wymagałoby wielu wywołań, to zaczęlibyśmy widzieć poprawę wydajności po użyciu VAO?
Steven Lu
Niestety link nie działa.
Nick Caplinger
3

Powyższa odpowiedź Roberta zadziałała, gdy ją wypróbowałem. W tym miejscu warto użyć kodu w Go używającego wielu obiektów atrybutów wierzchołków:

// 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()

Następnie w głównej pętli możesz ich używać jako takich:

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

Jeśli chcesz zobaczyć pełne źródło, jest dostępne jako Gist i pochodzi z przykładów w go-gl:

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

Dziękuję wszystkim za oryginalne odpowiedzi, miałem to samo pytanie co ECrownofFire.

Marek
źródło
3
Nie musisz odłączać vao1 przed związaniem vao2, wystarczy powiązanie vao2.
havokentity