#region OpenTK

OpenTKがGitHubで更新されるようになりました。NuGetから手に入れることができるようになりました。
現在は.NET Coreに対応する機運があるようです。

VBOの処理をカプセル化-VAO

VAOという概念

VAOとは、"Vertex Array Object"の略です。VBOと名前が似ていますが、役割は全く異なります。

VBOを使った描画では、描画時に場所の指定などをする処理が入るため、どうしても描画時の記述が多くなってしまいます。

その一連の処理をカプセル化するのが、VAOの使い方の一つです。頂点の情報に識別番号を付けて管理できるようにもなりますが、今回はそれは使いません。

VAOはOpenGL 3.0から標準機能になりましたが、同時にシェーダーを自作しないやり方は拡張機能となってしまったので、今回のやり方は来るシェーダーへのつなぎという側面が強いです。

VAOの使い方

VAOは名前の通りバッファではありません。そのため、VBOやIBOとは関数名や使い方が少し違います。

VAOを扱う関数の"例"を以下に示します。

GL.GenVertexArrays(int n, out int arrays)
Generate vertex array object names.
GL.BindVertexArray(int array)
Bind a vertex array object.
GL.DeleteVertexArrays(int n, ref int arrays)
Delete vertex array objects.

上から簡単に、VAOの作成、ひも付け、削除…です。

VAOのひも付けがされている中でVBOの設定をすると、その情報がVAOに記憶されるため、描画時にVAOをひも付けするだけでVBOの描画前の設定を全部やってくれます。

注意が必要なのは、IBOの全く関係しないというところです。IBOも使う場合は、VAOと一緒にひも付けする必要があります。

描画ごとの処理が簡単に見える

VAO
トーラスと球を描画

ソースコードは、OnLoad関数の処理が多くなってしまいましたが、OnRenderFrame関数の処理が簡単になっています。この書き方だと、素直にクラスを作れという話ですが、描画ごとの処理が簡単になることがよくわかると思います。

			//VAOを1コ作成
			GL.GenVertexArrays(1, out vao1);

			//ここからVAO1
			GL.BindVertexArray(vao1);

			//各Arrayを有効化
			GL.EnableClientState(ArrayCap.VertexArray);
			GL.EnableClientState(ArrayCap.NormalArray);
			GL.EnableClientState(ArrayCap.ColorArray);

			GL.BindBuffer(BufferTarget.ArrayBuffer, vbo1);

			//頂点の位置情報の場所を指定
			GL.VertexPointer(3, VertexPointerType.Float, Vertex.Size, 0);

			//頂点の法線情報の場所を指定
			GL.NormalPointer(NormalPointerType.Float, Vertex.Size, Vector3.SizeInBytes);

			//頂点の色情報の場所を指定
			GL.ColorPointer(4, ColorPointerType.Float, Vertex.Size, Vector3.SizeInBytes * 2);

			GL.BindBuffer(BufferTarget.ArrayBuffer, 0);

			GL.BindVertexArray(0);
			//VAO1ここまで

このコードの前に、VBOを作成してデータを送り込む処理が必要となります。

236行目でVAOを作成し、239行目でVAOをひも付けしています。259行目のように、0を指定することでひも付けを解除できます。

ひも付けの中で、GL.EnableClientStateを使ったりGL.VertexPointerなどを使ったりしています。ここでGL.EnableClientStateを使わなければならないのは、OpenGLの設計でサーバー側とクライアント側に分かれているということに関係しているようです。

実際、ひも付けの中でGL.EnableClientStateを使わない場合、何も描画してくれません。

今回は2つ物体を描画していますが、このコードは1つ分なのでもう1つの物体についても同様に処理を行います。

			GL.DeleteBuffers(1, ref vbo1);			//バッファを1コ削除
			GL.DeleteBuffers(1, ref ibo1);			//バッファを1コ削除
			GL.DeleteVertexArrays(1, ref vao1);		//VAOを1コ削除

			GL.DeleteBuffers(1, ref vbo2);			//バッファを1コ削除
			GL.DeleteBuffers(1, ref ibo2);			//バッファを1コ削除
			GL.DeleteVertexArrays(1, ref vao2);		//VAOを1コ削除

			GL.DisableClientState(ArrayCap.VertexArray);	//VertexArrayを無効化
			GL.DisableClientState(ArrayCap.NormalArray);	//NormalArrayを無効化
			GL.DisableClientState(ArrayCap.ColorArray);		//ColorArrayを無効化

GL.DeleteVertexArraysでVAOもしっかり削除します。

			GL.MatrixMode(MatrixMode.Modelview);

			GL.PushMatrix();
			GL.Translate(-1.5f, 0.0f, 0.0f);

			//1を描画
			GL.BindVertexArray(vao1);
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo1);
			GL.DrawElements(BeginMode.Quads, indices1.Length, DrawElementsType.UnsignedInt, 0);

			GL.PopMatrix();
			GL.Translate(1.5f, 0.0f, 0.0f);

			//2を描画
			GL.BindVertexArray(vao2);
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo2);
			GL.DrawElements(BeginMode.Quads, indices2.Length, DrawElementsType.UnsignedInt, 0);

			GL.BindVertexArray(0);

さて、描画処理ですが、1つの物体の表示は3行の処理で可能になっています。

VAOとシェーダーとのつながり

今回のプログラムは見栄えもパフォーマンスもよくなるというものではなく、OpenGLの仕様変更の関係で、このままの処理で使うことはまれです。

VAOはプログラマブルシェーダーで用いる場合がほとんどです。シェーダー(GPU側)が受ける情報と、プログラム(CPU側)が送る情報をリンクさせるのに使います。

VAOの生成やひも付けなどは一緒ですので、ぜひ覚えておいてください。

記事

外部リンク

Thanks