OpenGL 1.0 (1992)
一番最初は,Begin()からEnd()の間に,1頂点ずつ送るコマンドを使っていた.glBegin(GL_TRIANGLE_STRIP); glVertex2f( 0.0f, 0.5f); glVertex2f( 0.5f, 0.5f); glVertex2f(-0.5f, -0.5f); glEnd();
OpenGL 1.1 (1997)
glVertexPointer()により,頂点をまとめて送れるようになる.// 直接描画 GLfloat vertices[] = { 0.0f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f, }; glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer( 2, // 要素数 GL_FLOAT, // 型 0, // stride (0なので詰めてある想定になる) vertices ); glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
更に,インデックス指定による頂点の共有が可能に.この場合は,glDrawElements()で描画を行う.
// インデックス指定による描画 GLfloat vertices[] = { -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f, }; GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vertices); glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_SHORT, indices);
四角形を書くのに三角形2つを使う場合,6個の頂点が必要だったのが,これで4つで済むように.
OpenGL 1.5 (2003)
glEnableClientState()という名前からも分かるように,今までは頂点情報をCPU側に置いていたが,GPU側に頂点バッファオブジェクト(Vertex Buffer Object, VBO)を用意し,データを格納できる機能が追加される.// 直接描画 // バッファの生成 GLuint buffer; glGenBuffers(1, &buffer); // バッファの種類を設定 glBindBuffer(GL_ARRAY_BUFFER, buffer); // バッファにデータを設定 GLfloat vertices[] = { 0.0f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f, }; glBufferData( GL_ARRAY_BUFFER, sizeof vertices, vertices, GL_STATIC_DRAW ); // バッファのデータと関連づけ glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, 0); // 描画 glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); // バッファの削除 glDeleteBuffers(1, &buffer);
// インデックス指定による描画 // 頂点用バッファの生成 GLuint vertexBuffer; glGenBuffers(1, &vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); // インデックス用バッファの生成 GLuint indexBuffer; glGenBuffers(1, &indexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); GLfloat vertices[] = { -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f, }; glBufferData( GL_ARRAY_BUFFER, sizeof vertices, vertices, GL_STATIC_DRAW ); GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof indices, indices, GL_STATIC_DRAW ); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, 0); glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_SHORT, 0); glDeleteBuffers(1, &vertexBuffer); glDeleteBuffers(1, &indexBuffer);
OpenGL 2.0 (2004)
プログラマブルシェーダの登場により,頂点シェーダとバッファを関連づける機能が追加される.とりあえず頂点位置を指定するだけの簡単な頂点シェーダ.
#version 110 attribute vec4 position; void main(void) { gl_Position = position; }
頂点シェーダに関連付けて三角形を書く.
// バッファの生成 GLuint buffer; glGenBuffers(1, &buffer); // バッファの種類を設定 glBindBuffer(GL_ARRAY_BUFFER, buffer); // バッファにデータを設定 GLfloat vertices[] = { 0.0f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f, }; glBufferData( GL_ARRAY_BUFFER, sizeof vertices, vertices, GL_STATIC_DRAW ); // アトリビュート変数のインデックスを取得 GLuint position = glGetAttribLocation(program, "position"); // アトリビュート変数を有効化 glEnableVertexAttribArray(position); // アトリビュート変数の設定 glVertexAttribPointer(position, 2, GL_FLOAT, 0, 0); glDrawArrays(GL_TRIANGLES, 0, 3);
OpenGL 3.0 (2008)
バッファをバインドして,glEnableVertexAttribArray()で有効化して,glVertexAttribPointer()で位置を指定して,というのを切り替える度に実行しないといけないのは面倒だよね,ということで,それらをまとめるための頂点配列オブジェクト(Vertex Array Object, VAO)が導入される.とりあえず,頂点位置以外に頂点色も指定できる単純な頂点シェーダ.
#version 130 in vec4 position; in vec4 color; out vec4 oColor; void main(void) { gl_Position = position; oColor = color; }
良い感じにVAOを活用した短いコードを思いつかなかったので,適当に赤,緑,青を切り替えられるプログラム.
// 現在のプログラムの取得 GLuint program; glGetIntegerv(GL_CURRENT_PROGRAM, reinterpret_cast<GLint*>(&program)); // 頂点配列オブジェクトの生成 GLuint VAOs[3]; glGenVertexArrays(3, VAOs); // 最初の頂点配列オブジェクトのバインド glBindVertexArray(VAOs[0]); GLfloat const vertices[] = { 0.0f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f, }; // 頂点バッファの生成,バインド,データ設定 GLuint vertexBuffer; glGenBuffers(1, &vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glBufferData( GL_ARRAY_BUFFER, sizeof vertices, vertices, GL_STATIC_DRAW ); // 頂点シェーダのin変数positionの設定 GLint position = glGetAttribLocation(program, "position"); glEnableVertexAttribArray(position); glVertexAttribPointer( position, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, 0 ); // カラーバッファの生成,バインド,データ設定 GLuint colorBuffers[3]; glGenBuffers(3, colorBuffers); GLfloat const r[] = { 1.f, 0.f, 0.f, 1.f, 1.f, 0.f, 0.f, 1.f, 1.f, 0.f, 0.f, 1.f, }; glBindBuffer(GL_ARRAY_BUFFER, colorBuffers[0]); glBufferData(GL_ARRAY_BUFFER, sizeof r, r, GL_STATIC_DRAW); // 頂点シェーダのin変数colorの設定 GLint color = glGetAttribLocation(program, "color"); glEnableVertexAttribArray(color); glVertexAttribPointer( color, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, 0 ); // 2つ目の頂点配列オブジェクトのバインド glBindVertexArray(VAOs[1]); { // 頂点バッファのバインドやin変数の設定など glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glEnableVertexAttribArray(position); glVertexAttribPointer( position, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, 0 ); GLfloat const g[] = { 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, }; glBindBuffer(GL_ARRAY_BUFFER, colorBuffers[1]); glBufferData(GL_ARRAY_BUFFER, sizeof g, g, GL_STATIC_DRAW); glEnableVertexAttribArray(color); glVertexAttribPointer( color, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, 0 ); } // 3つ目の頂点配列オブジェクトのバインド glBindVertexArray(VAOs[2]); { // 頂点バッファのバインドやin変数の設定など glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glEnableVertexAttribArray(position); glVertexAttribPointer( position, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, 0 ); GLfloat const b[] = { 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, }; glBindBuffer(GL_ARRAY_BUFFER, colorBuffers[2]); glBufferData(GL_ARRAY_BUFFER, sizeof b, b, GL_STATIC_DRAW); glEnableVertexAttribArray(color); glVertexAttribPointer( color, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, 0 ); } glBindVertexArray(0); glClearColor(0.f, 0.f, 0.f, 1.f); glClear(GL_COLOR_BUFFER_BIT); // 描画前にバインドする頂点配列オブジェクトを切り替えると, // 色が変わる. glBindVertexArray(VAOs[0]); glDrawArrays(GL_TRIANGLES, 0, 3); glBindVertexArray(0); glFlush(); glDeleteBuffers(3, colorBuffers); glDeleteBuffers(1, &vertexBuffer); glDeleteVertexArrays(3, VAOs);
最近のOpenGLの入門書だと最初からシェーダを用意して,VAO用意して,VBO用意して,ようやく描画,という感じのコードが載ってたりして,初心者からすると非常に難しそうに思える気がする.でも,こうやって歴史を辿ってみると,何となく必要性が分かったような気がしないでもない.
3.0以降の変化は気が向いたら調べよう.VAOの例を考えてたら疲れた.
0 件のコメント:
コメントを投稿