OpenGLとグラフィックスプログラミングは初めてです。私はこれまでに非常に徹底的でよく書かれた教科書を読んでいますが、コードの中で私が理解していない点にぶつかったので、これらの行を理解したいと思います進む。
GLuint abuffer;
glGenVertexArrays(1, &abuffer);
glBindVertexArray(abuffer);
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
この本は、最初の3行がvertex-array objectを作成していることを説明しています。これは、関連データを頂点配列にバンドルするために使用されます。 2行目は未使用の名前を見つけ(abuffer
に格納されている符号なし整数の識別子を推測しています)、3行目はオブジェクトを作成/アクティブにします。
この本は、データを保存するためにバッファオブジェクトを作成する4-7行目、5行目は未使用の識別子(頂点配列オブジェクトの2行目と同様)、6行目を作成すると説明しています。バッファーを作成し、7行目でCPUに十分なメモリを割り当て、GL_STATIC_DRAW
のデータ(ポイント)へのポインターを作成します。
オブジェクトがアクティブであるとはどういう意味ですか?いつabuffer
を使用しますか?頂点配列が関連付けられたデータをバンドルすることはどういう意味ですか?また、この頂点配列オブジェクトにデータが関連付けられたのはいつですか?
abuffer
とbuffer
の関係について混乱しています。頂点配列とバッファオブジェクトとの関係、およびその関係が形成される時点について混乱しています。それらが実際に関連しているかどうかはわかりませんが、教科書には次々と掲載されています。
助けていただければ幸いです。ありがとう。
低レベルの観点からは、配列には2つの部分があると考えることができます。
配列のサイズ、形状、およびタイプに関する情報(たとえば、4つの要素を持つベクトルの行を含む32ビット浮動小数点数)。
配列データ。これは大きなバイトの塊にすぎません。
低レベルの概念はほとんど変わっていませんが、配列の指定方法は長年にわたって数回変更されています。
これがおそらくが今日物事を行うべき方法です。 OpenGL 3.xを実行できないが、それでもソフトウェアにお金を使うことができる人を見つけることは非常にまれです。
OpenGLのバッファオブジェクトはビットの大きな塊です。 「アクティブ」バッファを単なるグローバル変数と考えてください。パラメータを使用する代わりにアクティブバッファを使用する関数がたくさんあります。これらのグローバルな状態変数は、OpenGLのい側面です(直接アクセスする前は、以下で説明します)。
_GLuint buffer;
// Generate a name for a new buffer.
// e.g. buffer = 2
glGenBuffers(1, &buffer);
// Make the new buffer active, creating it if necessary.
// Kind of like:
// if (opengl->buffers[buffer] == null)
// opengl->buffers[buffer] = new Buffer()
// opengl->current_array_buffer = opengl->buffers[buffer]
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Upload a bunch of data into the active array buffer
// Kind of like:
// opengl->current_array_buffer->data = new byte[sizeof(points)]
// memcpy(opengl->current_array_buffer->data, points, sizeof(points))
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
_
さて、典型的な頂点シェーダーは、入力としてvertexesを取り、ビットの大きな塊ではありません。そのため、ビットのブロブ(バッファー)を頂点にデコードする方法を指定する必要があります。それがアレイの仕事です。同様に、単なるグローバル変数と考えることができる「アクティブな」配列があります。
_GLuint array;
// Generate a name for a new array.
glGenVertexArrays(1, &array);
// Make the new array active, creating it if necessary.
glBindVertexArray(array);
// Make the buffer the active array buffer.
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Attach the active buffer to the active array,
// as an array of vectors with 4 floats each.
// Kind of like:
// opengl->current_vertex_array->attributes[attr] = {
// type = GL_FLOAT,
// size = 4,
// data = opengl->current_array_buffer
// }
glVertexAttribPointer(attr, 4, GL_FLOAT, GL_FALSE, 0, 0);
// Enable the vertex attribute
glEnableVertexAttribArray(attr);
_
OpenGL 2.xでは、頂点配列はなく、データはグローバルのみでした。 glVertexAttribPointer()
とglEnableVertexAttribArray()
を呼び出す必要がありましたが、それらを呼び出す必要がありました毎回バッファを使用しました。 OpenGL 3.xでは、配列を一度セットアップするだけです。
OpenGL 1.5に戻ると、実際にはバッファを使用できますが、個別の関数を使用して各種類のデータをバインドしました。たとえば、glVertexPointer()
は頂点データ用で、glNormalPointer()
は通常データ用です。 OpenGL 1.5より前のバージョンでは、バッファはありませんでしたが、アプリケーションメモリへのポインタを使用できました。
4.3では、またはARB_vertex_attrib_binding拡張機能がある場合、属性形式と属性データを個別に指定できます。異なるバッファー間で1つの頂点配列を簡単に切り替えることができるため、これは素晴らしいことです。
_GLuint array;
// Generate a name for a new array array.
glGenVertexArrays(1, &array);
// Make the new array active, creating it if necessary.
glBindVertexArray(array);
// Enable my attributes
glEnableVertexAttribArray(loc_attrib);
glEnableVertexAttribArray(normal_attrib);
glEnableVertexAttribArray(texcoord_attrib);
// Set up the formats for my attributes
glVertexAttribFormat(loc_attrib, 3, GL_FLOAT, GL_FALSE, 0);
glVertexAttribFormat(normal_attrib, 3, GL_FLOAT, GL_FALSE, 12);
glVertexAttribFormat(texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 24);
// Make my attributes all use binding 0
glVertexAttribBinding(loc_attrib, 0);
glVertexAttribBinding(normal_attrib, 0);
glVertexAttribBinding(texcoord_attrib, 0);
// Quickly bind all attributes to use "buffer"
// This replaces several calls to glVertexAttribPointer()
// Note: you don't need to bind the buffer first! Nice!
glBindVertexBuffer(0, buffer, 0, 32);
// Quickly bind all attributes to use "buffer2"
glBindVertexBuffer(0, buffer2, 0, 32);
_
OpenGL 4.5では、またはARB_direct_state_access拡張機能がある場合、glBindBuffer()
またはglBindVertexArray()
を呼び出す必要はなくなりました。配列とバッファを直接指定するだけです。最後に配列をバインドするだけで描画できます。
_GLuint array;
// Generate a name for the array and create it.
// Note that glGenVertexArrays() won't work here.
glCreateVertexArrays(1, &array);
// Instead of binding it, we pass it to the functions below.
// Enable my attributes
glEnableVertexArrayAttrib(array, loc_attrib);
glEnableVertexArrayAttrib(array, normal_attrib);
glEnableVertexArrayAttrib(array, texcoord_attrib);
// Set up the formats for my attributes
glVertexArrayAttribFormat(array, loc_attrib, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribFormat(array, normal_attrib, 3, GL_FLOAT, GL_FALSE, 12);
glVertexArrayAttribFormat(array, texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 24);
// Make my attributes all use binding 0
glVertexArrayAttribBinding(array, loc_attrib, 0);
glVertexArrayAttribBinding(array, normal_attrib, 0);
glVertexArrayAttribBinding(array, texcoord_attrib, 0);
// Quickly bind all attributes to use "buffer"
glVertexArrayVertexBuffer(array, 0, buffer, 0, 32);
// Quickly bind all attributes to use "buffer2"
glVertexArrayVertexBuffer(array, 0, buffer2, 0, 32);
// You still have to bind the array to draw.
glBindVertexArray(array);
glDrawArrays(...);
_
ARB_direct_state_accessは、多くの理由で素晴らしいです。 OpenGLが追跡している隠されたグローバル変数について考える必要がないように、配列とバッファのバインドを忘れることができます(描画時を除く)。 glCreateBuffer()
とglCreateArray()
は同時に両方を行うため、「オブジェクトの名前の生成」と「オブジェクトの作成」の違いを忘れることができます。
Vulkanはさらに先に進み、上記で作成した擬似コードのようなコードを作成してもらいます。次のようなものが表示されます。
_// This defines part of a "vertex array", sort of
VkVertexInputAttributeDescription attrib[3];
attrib[0].location = 0; // Feed data into shader input #0
attrib[0].binding = 0; // Get data from buffer bound to slot #0
attrib[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attrib[0].offset = 0;
// repeat for attrib[1], attrib[2]
_
あなたの本の解釈は完全に正しいわけではありません。頂点配列オブジェクトはデータを保存しません。これらは、フレームバッファオブジェクトのようなコンテナと呼ばれるオブジェクトのクラスです。他のオブジェクトをアタッチ/関連付けすることはできますが、データ自体は保存されません。そのため、これらはコンテキスト共有可能なリソースではありません。
基本的に、頂点配列オブジェクトは、OpenGL 3.0の頂点配列状態をカプセル化します。 OpenGL 3.1(GL_ARB_compatibility
の代わり)およびOpenGL 3.2+コアプロファイルから、glVertexAttribPointer (...)
やglDrawArrays (...)
などのコマンドが機能するためには、常にゼロ以外のVAOがバインドされている必要があります。バインドされたVAOは、これらのコマンドに必要なコンテキストを形成し、状態を永続的に保存します。
GL(および互換性)の古いバージョンでは、VAOによって保存された状態はグローバルステートマシンの一部でした。
GL_ARRAY_BUFFER
の「現在の」バインディングはnotであることに言及する価値もありますVAOが追跡する州。このバインディングはglVertexAttribPointer (...)
などのコマンドで使用されますが、VAOはポインターのみを格納するバインディングを格納しません( GL_ARB_vertex_attrib_binding
GL 4.3と共に導入された拡張これは少し複雑になるので、簡単にするために無視しましょう)。
VAOdoGL_ELEMENT_ARRAY_BUFFER
にバインドされているものを覚えているため、インデックス付きの描画コマンドglDrawElements (...)
関数など、期待どおり(egVAOは最後にバインドされた要素配列バッファーを再利用します)。
glVertexAttribPointer を呼び出すと、関係が作成されます。
GL_VERTEX_ARRAY_BINDING
およびGL_ARRAY_BUFFER_BINDING
は定数ですが、バインディングのグローバルな状態を指すことができます。画像内の定数(オレンジ)ではなく状態を参照しています。 glGet を使用して、さまざまなグローバル状態について調べます。
VertexArrayは、1つまたは複数の並列頂点に関する情報(配列バッファーを含む)をグループ化しています。
つかいます GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
with glGetVertexAttrib を使用して、設定されている属性配列バッファーを検索します。
glBindBuffer(GL_ARRAY_BUFFER はグローバル状態を設定しますGL_ARRAY_BUFFER_BINDING
glBindVertexArray はグローバル状態を設定しますGL_VERTEX_ARRAY_BINDING
OpenGLはステートフルインターフェイスです。それは悪く、時代遅れでいですが、それは屋にとっての遺産です。
頂点配列オブジェクトは、ドライバーが描画呼び出しのデータを取得するために使用できるバッファーバインディングのコレクションです。ほとんどのチュートリアルでは1つのみを使用し、複数のVAOの使用方法については説明しません。
バッファバインディングは、特にglVertexAttribPointer
メソッドの関連メソッドにそのバッファを使用するようにopenglに指示します。