glVertexAttribPointerの明確化


94

私がこれを正しく理解していることを確認したいだけです(私はSOチャットで尋ねますが、それはそこで死んでいます!):

バインドすることで「最新」にする頂点配列があり
、ターゲットにバインドするバッファーがあり、ターゲット
にデータをglBufferData 入力します。これにより、ターゲットにバインドされているもの、つまりバッファーにデータを入力します。
次にglVertexAttribPointer、データがどのようにレイアウトされているかを説明する呼び出しを行います。データはバインドされているものでありGL_ARRAY_BUFFER 、この記述子は元の頂点配列に保存されます

(1)私の理解は正しいですか?ドキュメントには、どのようにすべての相関関係について少しまばらです。

(2)ある種のデフォルトの頂点配列はありますか?私は忘れてしまったので/省略glGenVertexArraysしてglBindVertexArray、私のプログラムは、それなしで罰金を働きました。


編集:私はステップを逃しました... glEnableVertexAttribArray

(3)呼び出されたときにVertex AttribがVertex Arrayに関連付けられていますか?それから、どのVertex Arrayが現在バインドされているかに関係なく、いつでもglVertexAttribPointerそのAttribを有効/無効にできglEnableVertexAttribArrayますか?

または(3b)呼び出されたときに頂点配列に関連付けられた頂点属性はありますか?glEnableVertexAttribArrayしたがってglEnableVertexAttribArray、異なる頂点配列がバインドされているときに、異なる時間に呼び出すことにより、同じ頂点属性を複数の頂点配列に追加できますか?

回答:


210

いくつかの用語は少しずれています:

  • A Vertex Arrayは、float[]頂点データを含む単なる配列(通常は)です。何かにバインドする必要はありません。Vertex Array ObjectまたはVAO と混同しないでください。後で説明します。
  • AはBuffer Object、一般に呼ばれるVertex Buffer ObjectVBOが短いため頂点を格納するとき、または、あなただけ呼んでいるものですBuffer
  • 何も頂点配列に背を保存されません取得し、glVertexAttribPointer正確に同じように動作しglVertexPointerたりglTexCoordPointerだけではなく、指定された属性の仕事、あなたがあなた自身の属性を指定する番号を提供してもらいます。この値はとして渡しますindex。すべてのglVertexAttribPointer通話は、次回glDrawArraysまたはを呼び出すときにキューに入れられますglDrawElements。VAOバインドがある場合、VAOはすべての属性の設定を保存します。

ここでの主な問題は、頂点属性をVAOと混同していることです。頂点属性は、描画のために頂点、texcoord、法線などを定義する新しい方法です。VAOは状態を格納します。最初に、描画が頂点属性でどのように機能するかを説明し、次に、VAOを使用してメソッド呼び出しの数を削減する方法を説明します。

  1. シェーダーで使用する前に、属性を有効にする必要があります。たとえば、頂点をシェーダーに送信する場合は、最初の属性0として送信する可能性があります。そのため、レンダリングする前に、で有効にする必要がありますglEnableVertexAttribArray(0);
  2. 属性が有効になったので、使用するデータを定義する必要があります。そのためには、VBO-をバインドする必要がありますglBindBuffer(GL_ARRAY_BUFFER, myBuffer);
  3. これで属性を定義できます- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);。パラメータの順序:0は定義する属性、3は各頂点のサイズGL_FLOAT、タイプ、GL_FALSEつまり各頂点を正規化しないことを意味し、最後の2つのゼロは頂点にストライドまたはオフセットがないことを意味します。
  4. それで何かを描く- glDrawArrays(GL_TRIANGLES, 0, 6);
  5. 次に描画するものは属性0を使用しない場合があります(実際には使用しますが、これは例です)。これを無効にできます- glDisableVertexAttribArray(0);

これをglUseProgram()呼び出しでラップすると、シェーダーを適切に処理するレンダリングシステムができます。しかし、5つの異なる属性、頂点、texcoords、法線、カラー、ライトマップ座標があるとします。まず最初にglVertexAttribPointer、これらの各属性に対して1 回の呼び出しを行うことになるため、事前にすべての属性を有効にする必要があります。リストにある属性0〜4を定義するとします。あなたはそのようにそれらのすべてを有効にします:

for (int i = 0; i < 5; i++)
    glEnableVertexAttribArray(i);

そして、あなたは(あなたがすべて1 VBOと利用オフセット/ストライドに保存していない限り)属性ごとに異なるVBOsをバインドしなければならない、あなたは5つの異なるようにする必要がありglVertexAttribPointerからの呼び出し、glVertexAttribPointer(0,...);glVertexAttribPointer(4,...);それぞれライトマップ座標に頂点のために。

うまくいけば、そのシステムだけでも理にかなっています。次に、VAOを使用して、このタイプのレンダリングを実行するときに、VAOを使用してメソッド呼び出しの数を削減する方法を説明します。VAOを使用する必要がないことに注意してください。

A Vertex Array Object又はVAOは、すべての状態を格納するために使用されるglVertexAttribPointerコールとの各場合に標的としたVBOs glVertexAttribPointer呼び出しが行われたが。

を呼び出して生成しglGenVertexArraysます。必要なものをすべてVAOに保存するにはglBindVertexArrayそれをバインドしてから、完全な描画呼び出しを実行します。すべての描画バインド呼び出しは、VAOによってインターセプトされ、保存されます。VAOをバインド解除するにはglBindVertexArray(0);

あなたがオブジェクトを描画したい場合、あなたは再呼び出し、すべてのVBOが結合またはする必要はありません今glVertexAttribPointerの呼び出し、あなただけでVAOをバインドする必要がglBindVertexArrayコールしglDrawArraysたりglDrawElements、あなたがあなたかのようにまったく同じことを描画することがありますそれらすべてのメソッド呼び出しを行っていました。後でVAOのバインドを解除することもできます。

VAOをバインド解除すると、すべての状態は、VAOをバインドする前の状態に戻ります。VAOがバインドされている間に行った変更が保持されるかどうかはわかりませんが、テストプログラムで簡単に把握できます。glBindVertexArray(0);「デフォルト」のVAOへのバインドと考えることができると思います...


更新:誰かが私の実際のドローコールの必要性に注意を向けました。結局のところ、VAOを設定するときに実際にFULL Draw呼び出しを実行する必要はなく、すべてのバインディング要素を実行するだけです。なぜそれが以前に必要だったと思ったのかわかりませんが、現在は修正されています。


10
「頂点バッファーオブジェクト、またはVBO(単にバッファーオブジェクトと呼ばれることもあります)」呼ばれることもあります。これは単なるバッファオブジェクトであり、ユニフォームブロック、ピクセル転送、変換フィードバック、またはその他の用途に使用できる他のバッファオブジェクトと同じです。OpenGL仕様で、「頂点バッファーオブジェクト」として参照されることはありませんオリジナルの拡張仕様でさえ、それをそれと呼ぶことはありません。
Nicol Bolas

3
すばらしい答えです。これを書くために時間を割いてくれてありがとう!ただし、フォローアップの質問がいくつかあります。(1)「レンダリングする前に、属性を定義する前に、glEnableVertexAttribArray(0)を使用して有効にする必要がある」とおっしゃっていましたglVertexAttribPointer。呼び出しの前に有効にする必要がありますか?私のテストでは、順序は重要ではないようです。(2)私があなたを正しく理解している場合、頂点属性はグローバルであり、有効/無効の状態のみが現在バインドされているVAOに保存されますか?
mpen 2012年

1
(1)以前glDrawArraysまたはで有効にしたことがある限り、順序は重要ではないと思いますglDrawElements。私はそれを反映するように投稿を更新します(2)はい。ただし、保存されるのは有効化/無効化の状態だけではなく、それらの呼び出しに関連するすべてのものです。つまり、当時GL_ARRAY_BUFFERにバインドされたもの、タイプ、ストライド、およびオフセットです。基本的には、すべての頂点属性を、VAOで設定した方法に戻すのに十分な量が格納されています。
Robert Rouhani、2012年

2
はい、VAOは、ほとんどの描画メソッドをVAOのバインドに置き換えることができるように設計されています。すべてのエンティティが個別のVAOを持つことができ、それでも問題なく機能します。ただし、ユニフォームを更新し、独自のテクスチャをバインドする必要があります。また、シェーダーの属性がバインドされているのと同じ属性インデックスを使用する必要があります。これlayout(location = x)は、シェーダー内で行わglBindAttributeLocationれる場合でも、シェーダーのコンパイル時に行われる場合でも同じです。
Robert Rouhani

8
最近のOpenGLでは、デフォルトの頂点配列オブジェクトはもうありません。自分で作成する必要があります。そうしないと、アプリケーションが前方互換のコンテキストで機能しません。
Overv

3

呼び出されるAPIの用語とシーケンスは、非常に混乱します。さらに混乱しているのは、さまざまな側面(バッファー、一般的な頂点属性、シェーダー属性変数)がどのように関連付けられるかです。かなり良い説明はOpenGL-Terminologyを参照してください。

さらに、リンクOpenGL-VBO、shader、VAOは、必要なAPI呼び出しを使用した簡単な例を示しています。即時モードからプログラム可能なパイプラインに移行する場合に特に適しています。

それが役に立てば幸い。

編集:以下のコメントからわかるように、人々は仮定をして結論にジャンプすることができます。実は初心者にとってはかなり混乱しています。


かなり良い説明については、OpenGL用語を参照してください。」1分足らずで、すでに1つの誤った情報が見つかりました。「これらは、シェーダー変数(座標、色など)属性を処理します。」それらは「指数」と呼ばれていません。それらは「場所」です。頂点属性に「インデックス」があり、場所とは大きく異なるため、これは非常に重要な違いです。それはとてもひどいウェブサイトです。
Nicol Bolas

2
それは公平なコメントですが、完全に正確ではありません。あなたは、一般的な属性を定義するために、OpenGLのAPIを見ればglVertexAttribPointerをvoid glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)識別子は次のように呼ばれていますindex。プログラムのコンテキストで同じ識別子がlocationAPI glGetAttribLocationで呼び出されます。
ap-osd
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.