頂点配列オブジェクトとは何ですか?


114

:私はちょうどこのチュートリアルから今日OpenGLを学ぶために始めていますhttp://openglbook.com/the-book/
私は三角形を描く第2章になった、と私はVAOs除くすべてが理解(この頭字語OKでしょうか?)。チュートリアルには次のコードがあります。

glGenVertexArrays(1, &VaoId);
glBindVertexArray(VaoId);

コードが必要であることは理解していますが、コードが何をするのかわかりません。この時点を超えてVaoIdを使用することはありませんが(破棄する場合を除いて)、コードはそれなしでは機能しません。これは拘束する必要があるためだと思いますが、理由はわかりません。この正確なコードは、すべてのOpenGLプログラムの一部である必要がありますか?このチュートリアルでは、VAOについて次のように説明しています。

頂点配列オブジェクト(またはVAO)は、頂点属性が頂点バッファーオブジェクト(またはVBO)に格納される方法を説明するオブジェクトです。これは、VAOが頂点データを格納する実際のオブジェクトではなく、頂点データの記述子であることを意味します。頂点属性は、glVertexAttribPointer関数とその2つの姉妹関数glVertexAttribIPointerおよびglVertexAttribLPointerで記述できます。最初の関数については、以下で説明します。

VAOが頂点の属性をどのように記述するかわかりません。私はそれらをどのようにも説明していません。glVertexAttribPointerから情報を取得しますか?たぶんこれだと思います。VAOは単にglVertexAttribPointerからの情報の宛先ですか?

余談ですが、私がフォローしているチュートリアルは受け入れられますか?気をつけるべきことはありますか、それともより良いチュートリアルがありますか?

回答:


100

「頂点配列オブジェクト」は、愚かな名前のOpenGL ARB小委員会によって提供されます。

それを幾何学オブジェクトと考えてください。(昔はSGI Performerプログラマとして、私はそれらをジオセットと呼んでいました。)オブジェクトのインスタンス変数/メンバーは、頂点ポインター、通常のポインター、カラーポインター、属性Nポインターなどです。

VAOが最初にバインドされるとき、これらのメンバーを呼び出すことによって割り当てます

glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer...;
glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer...;

等々。有効になっている属性と指定したポインターはVAOに格納されます。

その後、VAOを再度バインドすると、それらすべての属性とポインターも最新になります。したがって、1回のglBindVertexArray呼び出しは、以前にすべての属性を設定するために必要なすべてのコードと同等です。独自の構造体やオブジェクトを作成せずに、関数やメソッド間でジオメトリを渡すのに便利です。

(一度のセットアップで複数回使用するのがVAOを使用する最も簡単な方法ですが、属性をバインドして、さらに多くの有効化/ポインター呼び出しを行うだけで属性を変更することもできます。VAOは定数ではありません。)

パトリックの質問に対する回答の詳細:

新しく作成されたVAOのデフォルトは、空(AFAIK)です。ジオメトリも頂点もありません。そのため、それを描画しようとすると、OpenGLエラーが発生します。「すべてをFalse / NULL /ゼロに初期化する」のように、これはかなり正気です。

あなたがglEnableClientState物事をセットアップするときにのみ必要です。VAOは、各ポインターの有効/無効状態を記憶しています。

はい、VAOは保存glEnableVertexAttribArrayglVertexAttribます。古い頂点、法線、色などの配列は属性配列と同じで、頂点==#0などです。


62
'「頂点配列オブジェクト」は、OpenGL ARB Subcommittee for Silly Namesによって提供されます。' はい、頂点配列バインディングを格納するオブジェクトのこのようなばかげた名前。
ニコルボーラス

2
また、VAOはすべてに関連していますglVertexAttribPointer
Patrick

2
コアプロファイルを使用している人のための一般的な頂点属性の使用に関する情報を追加してください。
オスカー

8
@NicolBolasよりよい名前は、VertexArrayMacroまたはそれに似たものになります。
bobobobo 2013

7
@NicolBolas "Vertex Array Object"はひどい名前です。これは、データを属性にバインドすることです。名前が示すように、それは頂点の配列についてではありません。名前にバインディングや属性への参照はありません。「頂点配列」はそれ自体が別の概念であるため、理解がさらに難しくなります。私見、「(頂点)属性結合オブジェクト」の方がわかりやすい。Geometry Objectも優れています。私はそれが好きではありませんが、少なくともオーバーロードされていません。
AkiRoss 2017年

8

頂点配列オブジェクトは、ワープロプログラムなどのマクロのようなものです。良い説明がここにあります

マクロだけ は、この属性をアクティブにする、そのバッファをバインドするなど、実行したアクションを記憶しglBindVertexArray( yourVAOId )いるだけです。を呼び出すと、属性ポインタバインディングとバッファバインディングが再生されます。

したがって、次の描画の呼び出しでは、VAOによってバインドされたものをすべて使用します。

VAOは頂点データを保存しません。いいえ。頂点データは、頂点バッファーまたはクライアントメモリの配列に格納されます。


19
-1:マクロとは異なります。もしそうなら、新しいVAOをバインドしても、新しいVAOが明示的にそれらの配列を無効に「記録」していない限り、以前のVAOによって有効にされた頂点配列は無効になりません。すべての OpenGLオブジェクトと同様に、VAO はコマンドではなく状態を保持します。コマンドは単に状態を変更しますが、オブジェクトにはデフォルトの状態が設定されています。そのため、新しく作成されたVAOをバインドすると、常にすべての属性無効になります。
Nicol Bolas 2013

6

私は常にVAOをOpenGLで使用されるデータバッファーの配列と考えています。最新のOpenGLを使用して、VAOおよび頂点バッファーオブジェクトを作成します。

ここに画像の説明を入力してください

//vaoB is a buffer
glGenVertexArrays(1, vaoB); //creates one VAO
glBindVertexArray(vao.get(0));
glGenBuffers(vbo.length, vbo, 0); //vbo is a buffer
glBindVertexArray(vao.get(1));
glGenBuffers(vbo1.length, vbo1, 0); //vbo1 is a buffer
glBindVertexArray(vao.get(2));
glGenBuffers(vbo2.length, vbo2, 0); //vbo2 is a buffer

次のステップは、データをバッファにバインドすることです:

glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER,vertBuf.limit()*4, vertBuf, GL_STATIC_DRAW); //vertf buf is a floatbuffer of vertices

この時点でOpenGLは次のように表示されます。

ここに画像の説明を入力してください

これで、glVertexAttribPointerを使用して、バッファ内のデータが何を表すかをOpenGLに通知できます。

glBindBuffer(GL_ARRAY_BUFFER, 0); //bind VBO at 0
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); //each vertex has 3 components of size GL_FLOAT with 0 stride (space) between them and the first component starts at 0 (start of data)

ここに画像の説明を入力してください

これでOpenGLはバッファにデータを持ち、データがどのように頂点に編成されるかを認識します。同じプロセスをテクスチャ座標などに適用できますが、テクスチャ座標には2つの値があります。

glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER,coordBuf.limit()*4, coordBuf, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);

次に、テクスチャをバインドして配列を描画します。VertおよびFragシェーダーを作成し、コンパイルしてプログラムにアタッチします(ここには含まれていません)。

glActiveTexture(textureID); //bind our texture
glBindTexture(GL_TEXTURE_2D, textureID);
glDrawArrays(GL_TRIANGLES,0,6); //in this case 6 indices are used for two triangles forming a square

5

VAOは、OpenGLパイプラインの頂点フェッチステージを表すオブジェクトであり、頂点シェーダーに入力を供給するために使用されます。

このように頂点配列オブジェクトを作成できます

GLuint vao;
glCreateVertexArrays(1, &vao);
glBindVertexArray(vao);

まず、簡単な例を見てみましょう。シェーダーコードでそのような入力パラメーターを検討してください

layout (location = 0) in vec4 offset; // input vertex attribute

この属性を入力するには、

glVertexAttrib4fv(0, attrib); // updates the value of input attribute 0

頂点配列オブジェクトはこれらの静的属性値を格納しますが、さらに多くのことができます。

頂点配列オブジェクトを作成したら、その状態の入力を開始できます。OpenGLに、提供するバッファーオブジェクトに格納されているデータを使用して自動的にデータを入力するように依頼します。各頂点属性は、いくつかの頂点バッファーバインディングの1つにバインドされたバッファーからデータをフェッチします。このためにを使用しますglVertexArrayAttribBinding(GLuint vao, GLuint attribindex, GLuint bindingindex)。また、glVertexArrayVertexBuffer()関数を使用して、バッファーを頂点バッファーバインディングの1つにバインドします。私たちはglVertexArrayAttribFormat()関数てデータのレイアウトとフォーマットを記述し、最後に次の呼び出しにより属性の自動入力を有効にしますglEnableVertexAttribArray()

頂点属性が有効な場合、OpenGLは、glVertexArrayVertexBuffer()およびで提供された形式と位置情報に基づいて、頂点シェーダーにデータをフィードします glVertexArrayAttribFormat()。属性が無効な場合、頂点シェーダーには、への呼び出しで提供する静的情報が提供されglVertexAttrib*()ます。

// First, bind a vertex buffer to the VAO
glVertexArrayVertexBuffer(vao, 0, buffer, 0, sizeof(vmath::vec4));

// Now, describe the data to OpenGL, tell it where it is, and turn on automatic
// vertex fetching for the specified attribute
glVertexArrayAttribFormat(vao, 0, 4, GL_FLOAT, GL_FALSE, 0);

glEnableVertexArrayAttrib(vao, 0);

そして、シェーダーのコード

layout (location = 0) in vec4 position;

結局のところ、に電話する必要がありますglDeleteVertexArrays(1, &vao)


OpenGL SuperBibleを読んで、理解を深めることができます。


3
DSAスタイルのOpenGLの使用を促進している人々を見るのは良いことです。
Nicol Bolas、2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.