グラフィック エンジンを構築しているときに、 arcsynthesisの優れたチュートリアルに取り組んでいますが、思ったほど VAO を理解していないことに気付きました。
チュートリアル第5章から。オブジェクトの詳細
バッファ バインディングと属性の関連付け
glBindBuffer(GL_ARRAY_BUFFER) は、レンダリング用の属性設定の一部ですが、そのリストにないことに気付くかもしれません。GL_ARRAY_BUFFER へのバインディングは VAO の一部ではありません。これは、glBindBuffer(GL_ARRAY_BUFFER) を呼び出したときに、バッファー オブジェクトと頂点属性の間の関連付けが行われないためです。この関連付けは、glVertexAttribPointer を呼び出したときに発生します。
glVertexAttribPointer を呼び出すと、OpenGL はこの呼び出しの時点で GL_ARRAY_BUFFER にバインドされているバッファを取得し、それを指定された頂点属性に関連付けます。GL_ARRAY_BUFFER バインディングは、glVertexAttribPointer が読み取るグローバル ポインターと考えてください。したがって、glVertexAttribPointer 呼び出しを行った後、必要なものを自由に GL_ARRAY_BUFFER にバインドすることも、何もバインドしないこともできます。最終的なレンダリングには何の影響もありません。そのため、VAO はどのバッファ オブジェクトがどの属性に関連付けられているかを保存します。ただし、GL_ARRAY_BUFFER バインディング自体は保存されません。
私は最初、最後の行「...しかし、GL_ARRAY_BUFFERバインディング自体を保存していません」を見逃していました。この行に気付く前に、glVertexAttribPointer が呼び出されると、現在バインドされているバッファーが保存されていると思いました。この知識が欠けていたので、メッシュ クラスを作成し、多数のメッシュが適切にレンダリングされたシーンを取得することができました。
そのコードの一部を以下に示します。draw 関数で glBindBuffer を呼び出さないことに注意してください。
// MESH RENDERING
/* ... */
/* SETUP FUNCTION */
/* ... */
// Setup vertex array object
glGenVertexArrays(1, &_vertex_array_object_id);
glBindVertexArray(_vertex_array_object_id);
// Setup vertex buffers
glGenBuffers(1, &_vertex_buffer_object_id);
glBindBuffer(GL_ARRAY_BUFFER, _vertex_buffer_object_id);
glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(vertex), &_vertices[0], GL_STATIC_DRAW);
// Setup vertex attributes
glEnableVertexAttribArray(e_aid_position);
glEnableVertexAttribArray(e_aid_normal);
glEnableVertexAttribArray(e_aid_color);
glEnableVertexAttribArray(e_aid_tex);
glVertexAttribPointer(e_aid_position, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, pos));
glVertexAttribPointer(e_aid_normal, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, norm));
glVertexAttribPointer(e_aid_color, 4, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, col));
glVertexAttribPointer(e_aid_tex, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, tex));
/* ... */
/* DRAW FUNCTION */
/* ... */
glBindVertexArray(_vertex_array_object_id);
glDrawArrays(GL_TRIANGLES, 0, _vertices.size());
ライティングを開始しようとしているので、すべての法線が正しいことを確認できるように、デバッグを作成したいと思いました。現在、フレームにレンダリングするすべての行をベクターに保存するだけです。このデータはフレームごとに変更される可能性が高いため、GL_DYNAMIC_DRAW を使用して、レンダリングする直前にデータを指定しています。
最初にこれを行ったとき、無限を指すだけのガベージラインが得られました。問題のあるコードは次のとおりです。
// DEBUG DRAW LINE RENDERING
/* ... */
/* SETUP FUNCTION */
/* ... */
// Setup vertex array object
glGenVertexArrays(1, &_vertex_array_object_id);
glBindVertexArray(_vertex_array_object_id);
// Setup vertex buffers
glGenBuffers(1, &_vertex_buffer_object_id);
glBindBuffer(GL_ARRAY_BUFFER, _vertex_buffer_object_id);
// Note: no buffer data supplied here!!!
// Setup vertex attributes
glEnableVertexAttribArray(e_aid_position);
glEnableVertexAttribArray(e_aid_color);
glVertexAttribPointer(e_aid_position, 3, GL_FLOAT, GL_FALSE, sizeof(line_vertex), (GLvoid*)offsetof(line_vertex, pos));
glVertexAttribPointer(e_aid_color, 4, GL_FLOAT, GL_FALSE, sizeof(line_vertex), (GLvoid*)offsetof(line_vertex, col));
/* ... */
/* DRAW FUNCTION */
/* ... */
glBindVertexArray(_vertex_array_object_id);
// Specifying buffer data here instead!!!
glBufferData(GL_ARRAY_BUFFER, _line_vertices.size() * sizeof(line_vertex), &_line_vertices[0], GL_DYNAMIC_DRAW);
glDrawArrays(GL_LINES, 0, _line_vertices.size());
上記で見逃した詳細を見つけるだけでなく、少し探した後、描画関数で glBufferData の前に glBindBuffer を呼び出すと、すべてがうまくいくことがわかりました。
これを考えると、メッシュレンダリングが最初に機能した理由について混乱しています。バッファ内のデータを変更した場合、glBindBuffer を再度呼び出す必要がありますか? または、バッファをバインドせず、運が悪かっただけで動作した場合、動作は未定義ですか?
OpenGL バージョン 3.0 をターゲットにしていることに注意してください。