#region OpenTK

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

動的にモデル変形-VBO[3]

形が変化するものを描画する

今回は、描画ごとに物体の形状が変化するものを描画させます。今までは、物体を別の角度から見ることはあっても、その形を変化させることはありませんでした。

そのため、一度物体のデータを送り込んでしまえば、それをずっと使い続ければよかったわけです。

その処理は特に難しいものでは無く、記述する場所とオプションを変更すればOKです。

構造体を用いる

形状が変化するだけではプログラミング面ではあまり変化がないため、今回は構造体を使って頂点情報を定義します。

それに伴って、VBOの数が1つになります。これは、一つのVBOに複数の頂点情報をまとめて格納できるためです。ただ、頂点情報の場所の指定(GL.VertexPointer等)の処理で注意が必要です。

カラフル(?)な波の描画

VBO[3]
波を描画

ソースコードは、OpenGLの処理自体の記述はあまり変わっていません。

	struct Vertex
	{
		public Vector3 position;
		public Vector3 normal;
		public Color4 color;

		public Vertex(Vector3 position, Vector3 normal, Color4 color)
		{
			this.position = position;
			this.normal = normal;
			this.color = color;
		}

		public static readonly int Size = Marshal.SizeOf(default(Vertex));
	}

Vertex(=頂点)には、一つのオブジェクトで位置、法線、色の情報を持たせます。

Sizeは、この構造体の一つのオブジェクトの大きさです。

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

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

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

今回はVBOもIBOも一つづつ使います。データを送り込む処理は、描画前に毎回行うことにしました。

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

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

バッファの開放は忘れずに。

			//波を毎回生成
			CreateWave(100, 100, 1.0f, 0.5f, 1.0f, 1.0f);


			//毎回描画前にデータを送り込む
			GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
			int vertexArraySize = vertices.Length * Vertex.Size;
			GL.BufferData<Vertex>(BufferTarget.ArrayBuffer, new IntPtr(vertexArraySize), vertices, BufferUsageHint.DynamicDraw);

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


			GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);

			//頂点の位置情報の場所を指定
			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);

			//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);

300行目と304行目でGL.BufferData関数を使って、描画ごとにデータを送ってますが、最後の引数に注目してください。BufferUsageHint.DynamicDrawと指定してあります。

これはバッファのアクセスに関する情報をGPUに伝え、より効果的な使い方をするような工夫です。DynamicDrawは、もっとも頻繁に内容が変わることを示します。

323行目、326行目、329行目で、ひも付けされたVBOの中の情報の位置を指定しています。今までのものは第3引数が0でしたが、これは0を指定することでデータがぎっしり詰まっていることを伝えていました(関数の仕様)。位置情報はVertex.Sizeごとに現れ、その中でのずれは0、法線情報はVertex.Sizeごとに現れ、その中でのずれはVertex3の大きさ、色情報はVertex.Sizeごとに現れ、その中でのずれはVertex3の大きさ×2、ということになります。

Stride/Offset
第3引数Stride(=Vertex.Size) 第4引数Offset(=0,Vector3.SizeInBytes等)

波のモデルの生成部分は、グラフィックスの処理とは関係が無いので詳しくは書きません。

中心からの距離と時間からある地点での高さを、関数を使って求め、その関数の導関数を使って法線を求めます。また、座標によって色を付けます。

記事

外部リンク

Thanks