#author("2018-10-26T17:42:45+09:00","default:pbcglab_user","pbcglab_user") Vertex Array Object(VAO)について #author("2019-10-17T14:41:52+09:00","default:pbcglab_user","pbcglab_user") Vertex Array Object(VAO)について(文責-出村) **VAOとは [#b3a4b43b] VBOを便利にできる.(現在編集中) *VAOとは [#b3a4b43b] 一言でいうとVBO(Vertex Buffer Object)を便利にできる機能です(VBOについては[[こちら>OpenGL - VBO]]を参照). VBOは情報を格納するためのバッファなので,描画のために複数種類の情報を利用する必要が出てきた場合は描画のタイミングで複数のVBOを紐づける必要があります.具体的には頂点情報と法線情報とインデックス情報と色情報のVBOをそれぞれ用意した場合,実際に描画する際にそれらすべてを関連付ける記述が必要になります. この問題を解決するために,あらかじめ紐づけておいた(一連の処理をカプセル化)したものがVAOです.使用することによってソースコードが見やすくなります. *使い方 [#g57dba59] 基本的には3つの関数を使います. 手順として,まず作成したVAOに紐づけを行った状態でVBOの各種設定を行い,VAOの紐づけを解放します.そのあと描画の際にVAOを紐づけるだけで自動的に登録しておいた各種設定がGPUに送られます. - VAOを作る void glGenVertexArrays(GLsizei n, GLuint* arrays); nは作りたい数で,arraysは作る場所のポインタです. - VAOを消す void glDeleteVertexArrays(GLsizei n, const GLuint* arrays); nは消したい数で,arraysは消したいVAOのポインタです. - VAOを紐づける void glBindVertexArray(GLuint array); VAO(array)に紐づけ,またarrayに0を指定することで解放できます. 解放はしなくても動作はしますが安全のためにしておくと良いです. *使用例 [#qdc348dd] 前提としてVBOはすでに作ってあるものとします. +初期化 #code(C){{ // VAOを作る(Gluint vao) glGenVertexArrays(1, &vao); // VAOを紐づけ glBindVertexArray(vao); // VBO各種設定 glBindBuffer(GL_ARRAY_BUFFER, vboPos); // 頂点VBO(vboPos) glEnableVertexArrayAttrib(vao, 0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, vboCol); // 色VBO(vboCol) glEnableVertexArrayAttrib(vao, 3); glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, vboNor); // 法線VBO(vboNor) glEnableVertexArrayAttrib(vao, 2); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIdx); // インデックスVBO(vboIdx) // VBO,VAOの解放 glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); }} VBOがもつ情報の属性が何なのか(頂点なのか法線なのか)は以下の関数で指定しています. void glVertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); indexは属性,sizeは1つのデータの大きさ(3次元ベクトルなら3),typeはデータ型,normalizedは[0-1]に正規化するかどうか,strideはデータ同士の間隔,pointerはデータのポインタを指定します.ただし今回のようにVBOを用いる場合,pointerはVBOの先頭からの位置を指定します.似たような関数で''glVertexAttribIPointer''や''glVertexAttribLPointer''がありますがこれらは整数型や倍精度型に特化したものなのでここでは使いません. 属性値はシェーダーが受け取って判別するために自由に設定できますが,今回は[[Nvidiaのbuilt-in attributes>https://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/attributes.php]]に則って設定しています(頂点:0 色:3 法線:2). また,注意点としてはVAOは生成したタイミングではいかなる属性も使用できません.使用を可能にするために以下の関数を使用しています. void glEnableVertexAttribArray(GLuint index); indexは使用可能にする属性です.ちなみに再び無効にするための void glDisableVertexAttribArray(GLuint index); もあります. +描画 #code(C){{ // VAOを紐づけ glBindVertexArray(vao); // 描画 glDrawElements(GL_LINES, line_size, GL_UNSIGNED_INT, 0); // VAO解放 glBindVertexArray(0); }} 今回は線を描画しています.見ての通り非常にシンプルですが,ここをシンプルにするためにVAOを使っている感じです.今回描画に使っている関数は以下ですが,''glDrawArrays''なども使用可能で,通常のVBOで描画する場合と変わりません. void glDrawElements( GLenum mode, GLsizei count, GLenum type, const GLvoid* indices); modeは描画モード,countは頂点数,typeは''indexの''型(頂点情報の型でないことに注意),indicesはインデックス配列ですがVBOを使用しているので0で大丈夫です. +後始末 #code(C){{ // VAOの削除 glDeleteVertexArrays(1, &vao); }} 要らなくなったら消します.