Open GL ES 超入門 【描画をしてみる】

2012.10.08

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

前回までのあらすじ

難攻不落のOpen GL ESを使った図形描画を攻略すべく、まずは準備として、絵を描く領域の確保までを行いました

今回は、武器を作るための第一歩として、簡単な図形を描画してみます

図形描画の基礎

まずは点を一つ

単なる点です。1次元です。まあ、これでも点描のように無数に配置すれば絵にはなりますが

次は点を二つ

二つの点をつなぐと線になりました。2次元です。ちょっと図形のようになってきました

次は点を3つ

おめでとう。あなたは平面を手に入れました

基本的な図形は、ほとんどすべて、この3点の平面を「うりゃおいうりゃおい」しながら、組み合わせていくと作ることができます

例えば四角

対角線を一本引くと、三角形が二つになりました。

例えば円

中心から超細い三角形をぐるりと回すことで、円に近似できます

例えばこんな多角形

このように細かく砕けばひたすら三角形に分割できます

どうやら、この三角形が一番原始的な平面図形になるようです。今回は、この三角形を描画してみます

とりあえず描画してみる

まずは頂点座標を定義してあげます

前回解説した通り、描画を行う箇所は、ここになります

public void onDrawFrame(GL10 arg0)

Open GL ESのデフォルトの座標系を覚えているでしょうか。原点は画面の中央になります。 原点を始点に三点取って三角形を描画してみましょう。x, y共に1.0で正規化されているので、floatで頂点座標の情報を定義します

// 3点の座標を取ります
float[] vertices = new float[3 * 3];

なんで3点しかとらないのに、3 * 3も必要なんだよ、と思われるかもしれません。自分も思いました

これは、1点の座標を表現するのに、(x, y, z)の情報を必要とするためです

当分は2次元の図形の描画のみ行うので、z座標は現時点で意味をなしませんが、3次元の図形や図形表示の前後を制御する場合には重要な要素になるので、さりげなく登場させておきます。ピ○サーが次の作品のキャラクターを登場させるようなものだと思ってください

int vertexIdx = 0;

// 原点
vertices[vertexIdx++] = 0.0f;		// x
vertices[vertexIdx++] = 0.0f;		// y
vertices[vertexIdx++] = 1.0f;		// z

// 右上
vertices[vertexIdx++] = 1.0f;
vertices[vertexIdx++] = 1.0f;
vertices[vertexIdx++] = 1.0f;

// 右下
vertices[vertexIdx++] = 1.0f;
vertices[vertexIdx++] = 0.0f;
vertices[vertexIdx++] = 1.0f;

座標はx, y, zの順序を守って定義します。Open GL ES APIを通して転送するために、FloatBufferという入れ物に入れてあげます

// JavaVMの外側のシステムリソースを直接取りに行きます。4はfloatのサイズです
ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
// CPUのアーキテクチャによって、エンディアンが異なります(値の格納の仕方!)。これを呼ぶことで自動的に合わせてくれます
bb.order(ByteOrder.nativeOrder());

// 取得したByteBufferをFloatBufferとして使います。Floatの値をぶち込める入れ物みたいなものかと。
FloatBuffer fb = bb.asFloatBuffer();
fb.put(vertices);

// Bufferの先頭を指すように
fb.position(0);

特に気を付けるべきは

bb.order(ByteOrder.nativeOrder());

これを忘れるといくら描画しても、何も描画されません(何度かやらかしました) 通常Androidプログラミングをしている際は、あまりエンディアンを意識する機会がないので忘れがちです

頂点座標を、転送するためのFloatBufferに入れてあげたので、転送します

// 頂点座標を有効にしてくださーい
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

// 3=(x, y, z)のこと。2次元図形で(x, y)しか定義していない場合は、2
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, fb);

ようやく準備が整いました。いざ描画!

// 「頂点座標の3つの要素を使って、三角形を塗りつぶして描画してください」
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);

描画結果

うまく描画できました

塗りつぶしたくない場合は、次の指定をするとワイヤーフレーム表示になります

// 「頂点座標の3つの要素を線で結んで、始点と終点をつないでください」
gl.glDrawArrays(GL10.GL_LINE_LOOP, 0, 3);

今回のソース [OpenGLSampleRenderer.java]

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView.Renderer;
import android.util.Log;

public class OpenGLSampleRenderer implements Renderer {

	@Override
	public void onDrawFrame(GL10 gl) {
		// Frame毎に呼び出される描画メソッド
		
		// 描画内容をクリアする
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		Log.d(getClass().getCanonicalName(), "onDrawFrame : " + System.currentTimeMillis());
		
		// 3点の座標を取ります
		float[] vertices = new float[3 * 3];
		
		int vertexIdx = 0;

		// 原点
		vertices[vertexIdx++] = 0.0f;		// x
		vertices[vertexIdx++] = 0.0f;		// y
		vertices[vertexIdx++] = 1.0f;		// z

		// 右上
		vertices[vertexIdx++] = 1.0f;
		vertices[vertexIdx++] = 1.0f;
		vertices[vertexIdx++] = 1.0f;

		// 右下
		vertices[vertexIdx++] = 1.0f;
		vertices[vertexIdx++] = 0.0f;
		vertices[vertexIdx++] = 1.0f;
		
		// JavaVMの外側のシステムリソースを直接取りに行きます。4はfloatのサイズです
		ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
		// CPUのアーキテクチャによって、エンディアンが異なります(値の格納の仕方!)。これを呼ぶことで自動的に合わせてくれます
		bb.order(ByteOrder.nativeOrder());

		// 取得したByteBufferをFloatBufferとして使います。Floatの値をぶち込める入れ物みたいなものかと。
		FloatBuffer fb = bb.asFloatBuffer();
		fb.put(vertices);

		// Bufferの先頭を指すように
		fb.position(0);
		// 頂点座標を有効にしてくださーい
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

		// 3=(x, y, z)のこと。2次元図形で(x, y)しか定義していない場合は、2
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, fb);
		
		// 「頂点座標の3つの要素を使って、三角形を塗りつぶして描画してください」
		gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
		
	}

	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		// 大きさが変更されたときなどに呼び出される
		
		// 描画範囲を指定する
		gl.glViewport(0, 0, width, height);
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		
	}

}

とりあえず一番単純な図形がかけたので、次回は四角形、円など図形を描画するとともに、色もつけてみます

参考文献