#region OpenTK

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

複数の頂点属性-IBO/VBO[2]

IBOという概念

以前VBOを扱ったときは、頂点の位置だけを情報として保持していました。今回は法線と色情報を保持させたVBOを追加します。

また、それと同時にIBO(Index Buffer Object)というバッファも用います。

通常の3Dモデルデータは、頂点を複数のポリゴンが共有する場合がほとんどで、今までの方法ではポリゴンごとに同じ頂点を作成して描画しているので無駄が多く発生しています。その無駄を解消する仕組みは、頂点に対して配列のように番号を付けておいて、ポリゴンの描画時に使う頂点を番号(インデックス)で指定する、というものです。

簡単なモデルでは実感を得にくいですが、ソフトウェア側で頂点や法線のリストや配列を作り、番号を使って指定するよりは確実に速く動作します。また、一つの頂点の定義が一度で済むので、モデルデータを生成する立場から見ても楽になります。

IBOの使い方

IBOを使うには、VBO同様にバッファを生成するための関数を実行しますが、VBOのBufferTargetがArrayBufferなのに対し、IBOのBufferTargetはElementArrayBufferとなっています。

IBOとVBOはターゲットが違うため同時にひも付けができますが、不必要なときは両方のひも付けを解除しておくことを忘れないでください。

虹色ドーナツ

IBO/VBO[2]
カラフルなトーラスを描画

ソースコードは、バッファの数を増やしたので、それに伴って記述が増えました。

VBOはvbo[0]を位置情報用、vbo[1]を法線情報用、vbo[2]を色情報用として用います。

			//色を材質に変換
			GL.Enable(EnableCap.ColorMaterial);
			GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.Diffuse);

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

			//バッファを3コ生成
			GL.GenBuffers(3, vbo);

			GL.BindBuffer(BufferTarget.ArrayBuffer, vbo[0]);
			int positionArraySize = positions.Length * Marshal.SizeOf(default(Vector3));
			GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(positionArraySize), positions, BufferUsageHint.StaticDraw);

			GL.BindBuffer(BufferTarget.ArrayBuffer, vbo[1]);
			int normalArraySize = normals.Length * Marshal.SizeOf(default(Vector3));
			GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(normalArraySize), normals, BufferUsageHint.StaticDraw);

			GL.BindBuffer(BufferTarget.ArrayBuffer, vbo[2]);
			int colorArraySize = colors.Length * Marshal.SizeOf(default(Color4));
			GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(colorArraySize), colors, BufferUsageHint.StaticDraw);

			//バッファを1コ生成
			GL.GenBuffers(1, out ibo);

			GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo);
			int indexArraySize = indices.Length * sizeof(int);
			GL.BufferData(BufferTarget.ElementArrayBuffer, new IntPtr(indexArraySize), indices, BufferUsageHint.StaticDraw);

			//バッファのひも付けを解除
			GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);

ここで新しく出てきたものはColorMaterialです。前回は材質によって色を表現していましたが、今回は175行目と176行目で、材質のDiffuseに色情報を使うと指定します。

IBOは、BufferTargetの値をElementArrayBufferにすることで使用できます。ポリゴンの使用頂点がまとめられた、インデックス情報を持った配列を用います。

今回はGL.BindBufferの第2引数やGL.BindBufferの第1引数に気を付けてください。

			GL.DeleteBuffers(3, vbo);			//バッファを3コ削除
			GL.DeleteBuffers(1, ref ibo);		//バッファを1コ削除

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

増えた分のバッファやOpenGLの配列の許可は、終了時にしっかり解除するようにしましょう。

			//頂点の位置情報の場所を指定
			GL.BindBuffer(BufferTarget.ArrayBuffer, vbo[0]);
			GL.VertexPointer(3, VertexPointerType.Float, 0, 0);

			//頂点の法線情報の場所を指定
			GL.BindBuffer(BufferTarget.ArrayBuffer, vbo[1]);
			GL.NormalPointer(NormalPointerType.Float, 0, 0);

			//頂点の色情報の場所を指定
			GL.BindBuffer(BufferTarget.ArrayBuffer, vbo[2]);
			GL.ColorPointer(4, ColorPointerType.Float, 0, 0);

			//IBOを使って描画
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo);
			GL.DrawElements(BeginMode.Quads, indices.Length, DrawElementsType.UnsignedInt, 0);

			GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);

GL.VertexPointer関数、GL.NormalPointer関数、GL.ColorPointer関数を使う前に、対応する変数をひも付けしておきます。

重要なのは310行目です。これはひも付けされたIBOに従って、第1引数の図形で描画します。第2引数は使用する頂点インデックスの長さ、第4引数は頂点インデックスの開始位置です。

補足など

1MpolygonTorus
100万ポリゴンのトーラス

上のサンプル画像では、ちょっとぎこちなく描画してしまうグローシェーディングですが、ポリゴンの数を増やすときれいに見えます。

記事

外部リンク

Thanks