web-dev-qa-db-ja.com

glVertexAttribPointerの説明

私はこれを正しく理解していることを確認したいだけです(SO Chatでお願いしますが、そこは死んでいます!):

頂点配列があり、これをバインドすることで「現在」になります
次に、ターゲットにバインドするバッファーがあります
その後、glBufferDataを介してそのターゲットを満たします。これは、基本的にそのターゲットにバインドされたもの、つまりバッファ
次に、glVertexAttribPointerを呼び出します。これは、データがどのようにレイアウトされているかを示します。データは、GL_ARRAY_BUFFERそして、この記述子は元の頂点配列に保存されます

(1)私の理解は正しいですか?
documentation は、すべてがどのように相関するかについて少し疎です。

(2)ある種のデフォルトの頂点配列はありますか? glGenVertexArraysglBindVertexArrayを忘れてしまったので、私のプログラムはそれなしでもうまく機能したからです。


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

(3)glVertexAttribPointerが呼び出された時点で頂点配列に頂点配列が関連付けられており、どの頂点配列に関係なく、glEnableVertexAttribArrayを介してその属性をいつでも有効/無効にできます。現在バインドされていますか?

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

92
mpen

一部の用語は少しずれています。

  • Vertex Arrayは、頂点データを含む単なる配列(通常はfloat[])です。何にもバインドする必要はありません。後で説明するVertex Array ObjectまたはVAOと混同しないでください
  • Buffer Objectは、頂点(略してVBO)を格納するときに一般的にVertex Buffer Objectと呼ばれ、単にBufferと呼んでいます。
  • 頂点配列には何も保存されません。glVertexAttribPointerは、glVertexPointerまたはglTexCoordPointerとまったく同じように機能します。名前付き属性の代わりに、独自の属性を指定する番号を指定できます。この値をindexとして渡します。 glVertexAttribPointer呼び出しはすべて、次回glDrawArraysまたはglDrawElementsを呼び出すときにキューに入れられます。 VAOがバインドされている場合、VAOはすべての属性の設定を保存します。

ここでの主な問題は、頂点属性とVAOを混同していることです。頂点属性は、描画のために頂点、テックス座標、法線などを定義する新しい方法です。 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つの異なる属性、頂点、Texcoord、法線、色、およびライトマップ座標があるとします。まず、これらの属性のそれぞれに対して単一のglVertexAttribPointer呼び出しを行うことになり、事前にすべての属性を有効にする必要があります。リストに属性0〜4を定義するとします。次のようにすべてを有効にします。

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

そして、属性ごとに異なるVBOをバインドする必要があります(すべてを1つのVBOに格納し、オフセット/ストライドを使用しない限り)。次に、頂点のglVertexAttribPointer(0,...);からライトマップ座標までのglVertexAttribPointer(4,...);の5つの異なるglVertexAttribPointer呼び出しを行う必要があります。

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

Vertex Array ObjectまたはVAOは、すべてのglVertexAttribPointer呼び出しの状態と、glVertexAttribPointer呼び出しのそれぞれが行われたときにターゲットにされたVBOを格納するために使用されます。

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

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

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


更新:誰かが実際の描画呼び出しの必要性に気付いた。結局のところ、VAOをセットアップするときに実際にFULL描画呼び出しを実行する必要はなく、すべてのバインディングのことだけが必要です。以前に必要だと思った理由がわかりませんが、現在は修正されています。

207
Robert Rouhani

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

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

それが役に立てば幸い。

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

2
ap-osd