web-dev-qa-db-ja.com

OpenGL VAOのベストプラクティス

私はVAOに依存すると考えている問題に直面していますが、よくわかりません。

GL初期化は簡単でしたが、VAOの正しい使い方についてはわかりません。

glGenVertexArrays(1,&vao)

続いて

glBindVertexArray(vao)

その後、描画パイプラインでglBindBuffer()、glVertexAttribPointer()、glEnableVertexAttribArray()などを呼び出しました。最初にバインドされたVAOを気にせずに

これは正しい習慣ですか?

75
user815129

VAOは、バインド方法に関して、VBOおよびテクスチャと同様に機能します。プログラムの全長に対して単一のVAOをバインドしても、VAOをまったく使用せずにレンダリングするだけで済むため、パフォーマンス上の利点はありません。実際、頂点属性の設定が描画されるときに実装がどのようにインターセプトするかに応じて、処理が遅くなる場合があります。

VAOのポイントは、初期化中にオブジェクトを1回描画するために必要なすべてのメソッドを実行し、メインループ中に余分なメソッド呼び出しのオーバーヘッドをすべてカットすることです。ポイントは、複数の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をバインドする単一のメソッド呼び出しで置き換えます。

87
Robert Rouhani

いいえ、VAOの使い方ではありません。 VBO、テクスチャ、またはシェーダーの使用方法と同じ方法でVAOを使用する必要があります。最初に設定します。また、レンダリング中は、変更せずにバインドするだけです。

VAOを使用すると、次のことができます。

void Setup() {
    glGenVertexArrays(..);
    glBindVertexArray(..);
    // now setup all your VertexAttribPointers that will be bound to this VAO
   glBindBuffer(..);
   glVertexAttribPointer(..);
   glEnableVertexAttribArray(..);
}

void Render() {
    glBindVertexArray(vao);
    // that's it, now call one of glDraw... functions
    // no need to set up vertex attrib pointers and buffers!
    glDrawXYZ(..)
}

次のリンクも参照してください。

24

これは正しい習慣ですか?

はい、これは完全に合法かつ有効です。いいですか?まあ...

この種のことについては、いくつかの 非公式のパフォーマンステスト がありました。そして、少なくともこれがテストされたNVIDIAハードウェアでは、VAOの「適切な」使用(つまり、他の全員が提唱したもの)は実際にはslower多くの場合。これは、VAOを変更してもバインドされるバッファーが変更されない場合に特に当てはまります。

私の知る限り、AMDハードウェアで同様のパフォーマンステストは行われていません。一般に、何か変更されない限り、これはVAOの許容される使用方法です。

9
Nicol Bolas

上記のロバートの答えは、試してみたときにうまくいきました。ここで価値があるのは、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
Marek