前回はOpenGLからシェーダに頂点データを渡して三角形を表示しました。
今回の記事は、この頂点データの受け渡しを効率的に行うためのVBOという仕組みの説明をします。
VBOとは
前回はポリゴンを画面上に表示するために、OpenGLのプログラムで定義した頂点座標のデータをバーテックスシェーダに配列として渡していました。シェーダに頂点データを渡す場合、配列よりもVBOと呼ばれるものを使ったほうが効率が良くなります。VBOはVertex Berffer Objectの頭文字をとったもので、OpenGLからシェーダに渡す値を詰める専用オブジェクトのようなものです。
VBOを利用する流れは次のようになります。
- VBOの生成
- VBOに頂点データを格納する
- VBOと頂点シェーダのattribute変数を紐付ける
これだけでは、どのようにしてVBOを使えばよいのかイメージしにくいと思うので、早速プログラムを見ていきましょう。
VBOを使うプログラムを書く
次のプログラムは画面上に3角形を描画するプログラムです。必要な部分の解説はコメントに書いているので、あわせて読んでみてください。
#include <GLFW/glfw3.h> #include <iostream> #include <fstream> using namespace std; const int g_width = 640; const int g_height = 480; GLuint crateShader() { //バーテックスシェーダのコンパイル GLuint vShaderId = glCreateShader(GL_VERTEX_SHADER); string vertexShader = R"#( attribute vec3 position; void main(void){ gl_Position = vec4(position, 1.0); } )#"; const char* vs = vertexShader.c_str(); glShaderSource(vShaderId, 1, &vs, NULL); glCompileShader(vShaderId); //フラグメントシェーダのコンパイル GLuint fShaderId = glCreateShader(GL_FRAGMENT_SHADER); string fragmentShader = R"#( void main(void){ gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); } )#"; const char* fs = fragmentShader.c_str(); glShaderSource(fShaderId, 1, &fs, NULL); glCompileShader(fShaderId); //プログラムオブジェクトの作成 GLuint programId = glCreateProgram(); glAttachShader(programId,vShaderId); glAttachShader(programId,fShaderId); // リンク glLinkProgram(programId); glUseProgram(programId); return programId; } int main() { if( !glfwInit() ){ return -1; } GLFWwindow* window = glfwCreateWindow(g_width, g_height, "Simple", NULL, NULL); if (!window) { glfwTerminate(); return -1; } // モニタとの同期 glfwMakeContextCurrent(window); glfwSwapInterval(1); GLuint programId = crateShader(); // ゲームループ while (!glfwWindowShouldClose(window)) { // 画面の初期化 glClearColor(0.2f, 0.2f, 0.2f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClearDepth(1.0); // 頂点データ float vertex_position[] = { 0.0, 0.5, 0.4, -0.25, -0.4,-0.25, }; // VBOの生成 GLuint vbo; glGenBuffers(1, &vbo); // バッファをバインドする glBindBuffer(GL_ARRAY_BUFFER, vbo); // バッファにデータをセット glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float), vertex_position, GL_STATIC_DRAW); // 何番目のattribute変数か int attLocation = glGetAttribLocation(programId, "position"); // attribute属性を有効にする glEnableVertexAttribArray(attLocation); // attribute属性を登録 glVertexAttribPointer(attLocation, 2, GL_FLOAT, false, 0, NULL); // モデルの描画 glDrawArrays(GL_TRIANGLES, 0, 3); // バッファの入れ替え glfwSwapBuffers(window); // Poll for and process events glfwPollEvents(); } glfwTerminate(); return 0; }
上のプログラムを実行すると前回と同様、画面上に三角形が描画されます。
VBOの作成
頂点座標の定義に続けてVBOを作成します。VBOの生成はクラスをnewするイメージに近いですね。
// VBOの生成 GLuint vbo; glGenBuffers(1, &vbo);
glGenBuffersはVBOを作成するための関数です。第一引数には作成するvboの個数、第二引数にはvboのインデックスを渡します。ここでは作成するVBOは1つだけなので、第二引数にはポインタを渡しています。
VBOに頂点データを格納する
続いて、VBOに頂点データを格納します。VBOに頂点データを格納する前に、まずは今作成したVBOをバインドする必要があります。バインドとは、頂点データを格納するVBOを指定する作業です。
// バッファをバインドする glBindBuffer(GL_ARRAY_BUFFER, vbo); // バッファにデータをセット glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float), vertex_position, GL_STATIC_DRAW);
glBindBufferを使って、作成したVBOをOpenGLにバインドしてから、glBufferDataで作成したVBOに頂点情報を書き込んでいます。第二引数に全頂点情報のサイズ、第三引数には頂点情報へのポインタを渡しています。
VBOを使って三角形を描画する
これで、ようやくVBO経由で受け渡し可能な状態になったので、glVertexAttribPointer関数を使って、頂点情報をシェーダに渡しています。今回はVBOを使っているため、 最後の引数にはNULLを渡しています。
// attribute属性を登録 glVertexAttribPointer(attLocation, 2, GL_FLOAT, false, 0, NULL);
まとめ
今回はVBOを使ってポリゴンの表示を行いました。前回のプログラムと比べると、少々複雑になってしまいましたね。でも、やっていることは「OpenGLで定義した頂点データをVBO経由でシェーダに渡して描画している」だけです。