OpenGL ES 2.0で三角形を描く

AndroidのOpenGL ESテストの続きです。OpenGL ES 1.0による三角形の描画(固定機能パイプライン)に続いて、OpenGL ES 2.0による三角形の描画(自分で設定したシェーダープログラムによる描画)を試してみましょう。

OpenGL ES 2.0による三角形描画については、すでに当サイトでもOpenGL ES 2.0のHTML5(JavaScript)版とも言うべきWebGLで試したコードがあります。とりあえず、これをAndroid向けのJavaのコードに書き換えてみましょう。

AndroidとHTML5では、システム初期化や描画周りの設定コードが異なりますが、そのあたりはAndroidの開発者サイトにあるOpenGL ESによる各種初期化やシェーダーの設定、Javaのバッファからシェーダー変数に値を渡す処理といった一連の流れをまとめたサンプルを参考にすることにします。

AndroidのGLSurfaceViewで各種設定を行ったりやProgramオブジェクトを作るコードのサンプルをざっと見ると、メソッド名が微妙に違う(AndroidというかOpenGL的なAPIでは先頭にglが付く)ものの、流れとしてはWebGLとほぼ同じ形で出来そうですね。

OpenGL ES 2.0を使うための明示的な設定は、GLSurfaceViewの作成時にsetEGLContextClientVersion()で「2」を指定することくらいでしょうか。その後、Rendererを設定して主な処理はこちらで行います。

RendererのonSurfaceCreate()では、シェーダーを持たせたProgramと頂点データを格納したFloatBufferを作っておきましょう。Rendererの中でOpenGL ES 2.0周りの処理を行うには、引数に渡されたGL10オブジェクトを無視してGLES20クラスのスタティックメソッドを呼び出すというやや変則的な構造になるんですね。

コンパイルしてシェーダーに設定するGLSL ESのコード文字列については、WebGL版と同一で変更していません。Vertexシェーダーは、渡された2次元の頂点データをそのまま4次元に拡張するだけ、Fragmentシェーダーでは単に赤く着色するだけです。

実際に描画を行うGLSurfaceViewのonDrawFrame()では、VertexシェーダーコードのAttribute変数vPosに頂点を渡して三角形を描くよう指定します。

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.GLES20;
import android.opengl.GLSurfaceView.Renderer;

public class TestRenderer implements Renderer {

	// Vertexシェーダーコード
	private String vertexShaderCode = 
	        "attribute vec2 vPos; void main() { gl_Position = vec4(vPos, 0.0, 1.0); }";

	// Fragmentシェーダーコード
	private String fragmentShaderCode = 
	       "void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }";

	private int mProgram;

	// 頂点データ格納用バッファ
	private FloatBuffer mVertexBuffer;

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

		int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
		int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

		// Vertexシェーダーのコードをコンパイル
		GLES20.glShaderSource(vshader, vertexShaderCode);
		GLES20.glCompileShader(vshader);

		// Fragmentシェーダーのコードをコンパイル
		GLES20.glShaderSource(fshader, fragmentShaderCode);
		GLES20.glCompileShader(fshader);

		// Programを作成
		mProgram = GLES20.glCreateProgram(); 

		// Programのシェーダーを設定
		GLES20.glAttachShader(mProgram, vshader);
		GLES20.glAttachShader(mProgram, fshader);

		GLES20.glLinkProgram(mProgram);

		GLES20.glClearColor(0.0f, 0.0f, 0.5f, 1.0f);

		// 頂点データ作成
		float[] vertexList = {0.0f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f};

		// 頂点データをバッファに格納
		ByteBuffer bb = ByteBuffer.allocateDirect(vertexList.length * 4);
		bb.order(ByteOrder.nativeOrder());
		mVertexBuffer = bb.asFloatBuffer();
		mVertexBuffer.put(vertexList);
		mVertexBuffer.position(0);

	}
 
	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		GLES20.glViewport(0, 0, width, height);
	}

	@Override
	public void onDrawFrame(GL10 gl) {

		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

		GLES20.glUseProgram(mProgram);

		// VertexシェーダーコードのvPos変数の番号を取得
		int vPos = GLES20.glGetAttribLocation(mProgram, "vPos");
		GLES20.glEnableVertexAttribArray(vPos);

		// バッファとvPosを結びつける
		GLES20.glVertexAttribPointer(vPos, 2, GLES20.GL_FLOAT, false, 0, mVertexBuffer);

		// 描画する頂点をVertexシェーダーに指定
		GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);

		GLES20.glDisableVertexAttribArray(vPos);

	}

}

Activityでは、GLSurfaceViewを作成しRendererを設定してからsetContentView()します。

import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity {

	private GLSurfaceView mGLView;

	@Override
	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);

		mGLView = new GLSurfaceView(this);

		// OpenGL ES 2.0を使用
		mGLView.setEGLContextClientVersion(2);

		// Rendererを設定
		mGLView.setRenderer(new TestRenderer());

		setContentView(mGLView);

	}

	@Override
	public void onPause() {

		mGLView.onPause();

		super.onPause();

	}

	@Override
	public void onResume() {

		super.onResume();

		mGLView.onResume();

	}

}

Androidの実機で試すと、赤い三角形が表示されるはずです。SDKのエミュレータでは、GPU Suportの設定が必要かもしれません。


創作プログラミングの街