OpenGL ES2 でテクスチャ 〜Android版〜

AndroidOpenGL ES2に関しては、探すと結構いいのが見つかるので残さなくても良いかな?とも思ったけど、まぁ、メモ程度に残しておくことにした。iOS版と手順はほぼ同じ。ただ、eclipseではテンプレート作ってくれないので(作ってくれるのあると思うんだけど)、ぜーんぶ自分で作る必要があるところが違うかな。という訳で今回は長そうだ。
あ、importするものは、eclipseの補完機能で全部解決できると思うので省略する。


まず、Activityから。あ、プロジェクト名は適当で。とりあえずgltestなんて名前にしてみた。

public class GltestActivity extends Activity {
	GLSurfaceView glView;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        glView = new GLSurfaceView(this);
        glView.setEGLContextClientVersion(2);
        glView.setRenderer(new GLRenderer(glView));
        setContentView(glView);
    }
}

GLSurfaceViewを作成し、Rendererをセットします。このとき、セットするRendererは自前で作成するクラスにします。あとは、OpenGL ES2を使用することをGLSurfaceViewにsetEGLContextClientVersion()で設定します。


次にRendererを作っていきます。

import android.opengl.GLSurfaceView.Renderer;

public class GLRenderer implements Renderer {
・・・略・・・
}

中身は後で書くとして、GLRendererクラスはandroid.opengl.GLSurfaceView.Rendererを継承して作成するので、以下の3つは必ず実装します。

public void onDrawFrame(GL10 arg0)
public void onSurfaceChanged(GL10 gl, int width, int height)
public void onSurfaceCreated(GL10 gl, EGLConfig config)


まずは、onSurfaceChanged()から・・・といきたいところだけども、テクスチャ作成にviewが要るので、コンストラクタで渡してもらったGLSurfaceViewをストックしておきます。ついでに必要なものも作成しておきます。

private GLSurfaceView mView;
private FloatBuffer mVertexBuffer;
private FloatBuffer mTexcoordBuffer;
private final float vertexs[] = {
	-1.0f, -1.0f, 0.0f,  //left top
	-1.0f,  1.0f, 0.0f,   //left bottom
	1.0f, -1.0f, 0.0f,   //right top
	1.0f,  1.0f, 0.0f,   //right bottom
};
private final float texcoords[] = {
	0.0f, 0.0f,	//left top
	0.0f, 1.0f,	//left bottom
	1.0f, 0.0f,	//right top
	1.0f, 1.0f,	//right bottom
};

public GLRenderer(GLSurfaceView view)
{
	mView = view;
	mVertexBuffer = ByteBuffer.allocateDirect(vertexs.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
	mVertexBuffer.put(vertexs).position(0);
	mTexcoordBuffer = ByteBuffer.allocateDirect(texcoords.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
	mTexcoordBuffer.put(texcoords).position(0);
}

OpenGLAPIでvetexとtexcoordの配列を渡したいのだけれど、FloatBufferでないと渡せないので、これを作成しておきます。


onSurfaceChanged()を実装しましょう。やることは、シェーダの作成、テクスチャ作成、OpenGLの各設定です。

private int program;
private int mTexture;
private int shPosition;
private int shTexcoord;
private int shTexture;

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
	mTexture = loadTexture(R.drawable.back);
	int vertexShader = compileShader(GLES20.GL_VERTEX_SHADER,
								vertexShaderCode);
	int fragmentShader = compileShader(GLES20.GL_FRAGMENT_SHADER,
								fragmentShaderCode);
	program = GLES20.glCreateProgram();
	GLES20.glAttachShader(program, vertexShader);
	GLES20.glAttachShader(program, fragmentShader);
	GLES20.glLinkProgram(program);
	GLES20.glUseProgram(program);

	GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
						GLES20.GL_TEXTURE_MIN_FILTER,
						GLES20.GL_LINEAR);
	GLES20.glEnable(GLES20.GL_BLEND);
	GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA,
						GLES20.GL_ONE_MINUS_SRC_ALPHA);
	shPosition = GLES20.glGetAttribLocation(program, "position");
	shTexcoord = GLES20.glGetAttribLocation(program, "texcoord");
	shTexture = GLES20.glGetUniformLocation(program, "texture");
		
	GLES20.glEnableVertexAttribArray(shPosition);
	GLES20.glEnableVertexAttribArray(shTexcoord);
}


シェーダのコンパイルはこんな感じ

private int compileShader(int type, String code)
{
	int shader = GLES20.glCreateShader(type);
	GLES20.glShaderSource(shader, code);
	GLES20.glCompileShader(shader);
        //↓ここの部分はあってもなくても良い。コンパイルエラーのときのログが見られる。
	int logLength[] = new int[1];
	GLES20.glGetShaderiv(shader, GLES20.GL_INFO_LOG_LENGTH, logLength, 0);
	if (logLength[0] > 0) {
	    String log = GLES20.glGetShaderInfoLog(shader);
	    Log.e("GLRenderer", "Shader compile log:" + log);
	}
        //↑ここまで

	int status[] = new int[1];
	GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, status, 0);
	if (status[0] == 0) {
	    Log.e("GLRender", "Couldn't compile shader");
	    GLES20.glDeleteShader(shader);
	    return -1;
	}
	return shader;
}

glCompileShader()でシェーダをコンパイルして、glGetShaderiv()でステータスを確認しています。


テクスチャ作成はこんな感じ

private int loadTexture(int res)
{
	int texture[] = new int[1];
	Resources r = mView.getResources();
	Bitmap bmp;
	bmp = BitmapFactory.decodeResource(r, res);
	GLES20.glGenTextures(1, texture, 0);
	GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);
	GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);
	bmp.recycle();
	return texture[0];
}

テクスチャはビットマップデータから作成するので、BitmapFactoryを使ってビットマップを作成して、テクスチャを用意します。


最後に、描画。

public void onDrawFrame(GL10 arg0) {
	GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
	GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
	GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
	GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture);
	GLES20.glUniform1i(shTexture, 0);
	    
	GLES20.glVertexAttribPointer(shTexcoord, 2, GLES20.GL_FLOAT,
	    							 false, 0, mTexcoordBuffer);
	GLES20.glVertexAttribPointer(shPosition, 3, GLES20.GL_FLOAT,
	    							 false, 0, mVertexBuffer);
	GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);    
}

この辺はiOS版と同じなので。
今回はここまで。