KEIS BLOGは株式会社ケイズ・ソフトウェアが運営しています。

KEIS BLOG

[Android]OpenGL ESのひな型コード


OpenGLを知りたいなら、まずは動くコードをいじる事から始めてください。
基礎とか理論とか学習から入ってしまうと、退屈すぎて挫折するかもしれません。
それでは早速コードから。

activityのレイアウトにOpenGLのビューを追加します。

[layout/activity_main.xml]

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent" >

	<android.opengl.GLSurfaceView
		android:id="@+id/surfaceView1"
		android:layout_width="match_parent"
		android:layout_height="match_parent" />
</RelativeLayout>

activityでは、onCreate,onResume,onPauseでOpenGLのビューのメソッドを呼び出します。

[MainActivity.java]

package com.example.gles1_3;

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

public class MainActivity extends Activity {
	private GLSurfaceView glView;

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		glView = (GLSurfaceView)findViewById(R.id.surfaceView1);
		glView.setEGLConfigChooser(8, 8, 8, 8, 8, 8);
		glView.setRenderer(new MyRenderer());
	}
	protected void onResume() {
		super.onResume();
		glView.onResume();
	}
	protected void onPause() {
		super.onPause();
		glView.onPause();
	}
}

描画の実装は、activityでnewしたMyRendererクラスで行います。
物体を正常に表示させる為の必要最低限なコードは以下のとおりで、これがひな型になります。

[MyRenderer.java]

package com.example.gles1_3;

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.GLES11;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;

public class MyRenderer implements GLSurfaceView.Renderer {
	public void onDrawFrame(GL10 gl10) {
		// バッファクリア
		GLES11.glClear(GLES11.GL_COLOR_BUFFER_BIT | GLES11.GL_DEPTH_BUFFER_BIT);

		//物体データ
		float mVertices[] = { 0, 1, 0, -1, 0, 0,  1, 0, 0, };
		float mNormals[]  = { 0, 0, 1,  0, 0, 1,  0, 0, 1, };

		//物体の頂点を転送
		FloatBuffer vertBuf = ByteBuffer.allocateDirect(mVertices.length * 4)
		.order(ByteOrder.nativeOrder()).asFloatBuffer();
		vertBuf.put(mVertices).position(0);
		GLES11.glVertexPointer(3,GLES11.GL_FLOAT,0,vertBuf);

		//物体の法線を転送
		FloatBuffer normalBuf = ByteBuffer.allocateDirect(mNormals.length * 4)
		.order(ByteOrder.nativeOrder()).asFloatBuffer();
		normalBuf.put(mNormals).position(0);
		GLES11.glNormalPointer(GLES11.GL_FLOAT, 0, normalBuf);

		//物体の色を設定
		GLES11.glMaterialfv(GLES11.GL_FRONT_AND_BACK, GLES11.GL_DIFFUSE,
			new float[]{1,1,0,0},0);
		GLES11.glMaterialfv(GLES11.GL_FRONT_AND_BACK, GLES11.GL_AMBIENT,
			new float[]{.1f,.1f,.1f,.0f},0);
		GLES11.glMaterialfv(GLES11.GL_FRONT_AND_BACK, GLES11.GL_SPECULAR,
			new float[]{.5f,.5f,.5f,.5f},0);
		GLES11.glMaterialfv(GLES11.GL_FRONT_AND_BACK, GLES11.GL_SHININESS,
			new float[]{64.0f},0);

		//カメラの設定
		GLES11.glMatrixMode(GLES11.GL_MODELVIEW);
		GLES11.glLoadIdentity();
		GLU.gluLookAt(gl10, 0,0,3, 0,0,0, 0,1,0);

		//物体の配置を決める
		GLES11.glRotatef(0, 1.0f, 0.0f, 0.0f);		 

		//描画する
		GLES11.glDrawArrays(GLES11.GL_TRIANGLES,0,mVertices.length/3);
	}
	public void onSurfaceChanged(GL10 gl10, int mWidth, int mHeight) {
		// ビューポート設定
		GLES11.glViewport(0, 0, mWidth, mHeight);

		// 透視変換設定
		GLES11.glMatrixMode(GLES11.GL_PROJECTION);
		GLES11.glLoadIdentity();
		GLES11.glFrustumf(-1,1,-1,1,1f,100f);
	}
	public void onSurfaceCreated(GL10 arg0, EGLConfig arg1) {
		// 各配列の有効化
		GLES11.glEnableClientState(GLES11.GL_VERTEX_ARRAY);
		GLES11.glEnableClientState(GLES11.GL_NORMAL_ARRAY);

		// カリング設定
		GLES11.glEnable(GLES11.GL_CULL_FACE);
		GLES11.glFrontFace(GLES11.GL_CCW);

		//ライトの設定
		GLES11.glEnable(GLES11.GL_LIGHTING);
		int light = GLES11.GL_LIGHT0;
		GLES11.glEnable(light);
		GLES11.glLightfv(light,GLES11.GL_POSITION, new float[]{.0f,.0f,.5f,.0f},0);
		GLES11.glLightfv(light,GLES11.GL_DIFFUSE,  new float[]{.6f,.6f,.6f,.0f},0);
		GLES11.glLightfv(light,GLES11.GL_AMBIENT,  new float[]{.6f,.6f,.6f,.0f},0);
		GLES11.glLightfv(light,GLES11.GL_SPECULAR, new float[]{.5f,.5f,.5f,.0f},0);
	}
}

このひな型コードには、物体データとして三角形が定義してあります。
なので実行するとこのように表示されます。

triangle

今度は物体データを差し替え、別の物体を表示させます。
データ作成にはモデリングソフトを使いますが、説明がメンドイので
予め下茹でしたものがこちらでございます。

[MyRenderer.java]

	//物体データ
	float f0=0.000000f,f1=1.000000f,f2=0.723600f,f3=0.447215f,f4=0.525720f;
	float f5=0.276385f,f6=0.850640f,f7=0.894425f,f8=0.187597f,f9=0.794651f;
	float fa=0.577354f,fb=0.607065f,fc=0.794652f,fd=0.491122f,fe=0.356829f;
	float ff=0.982246f,fg=0.303536f,fh=0.187589f,fi=0.934171f,fj=0.794649f;
	float fk=0.187587f,fl=0.577359f;
	float mVertices[]={
	 f0,-f1, f0, f2,-f3, f4,-f5,-f3, f6, f2,-f3, f4, f0,-f1, f0, f2,-f3,-f4,
	 f0,-f1, f0,-f5,-f3, f6,-f7,-f3, f0, f0,-f1, f0,-f7,-f3, f0,-f5,-f3,-f6,
	 f0,-f1, f0,-f5,-f3,-f6, f2,-f3,-f4, f2,-f3, f4, f2,-f3,-f4, f7, f3, f0,
	-f5,-f3, f6, f2,-f3, f4, f5, f3, f6,-f7,-f3, f0,-f5,-f3, f6,-f2, f3, f4,
	-f5,-f3,-f6,-f7,-f3, f0,-f2, f3,-f4, f2,-f3,-f4,-f5,-f3,-f6, f5, f3,-f6,
	 f2,-f3, f4, f7, f3, f0, f5, f3, f6,-f5,-f3, f6, f5, f3, f6,-f2, f3, f4,
	-f7,-f3, f0,-f2, f3, f4,-f2, f3,-f4,-f5,-f3,-f6,-f2, f3,-f4, f5, f3,-f6,
	 f2,-f3,-f4, f5, f3,-f6, f7, f3, f0, f5, f3, f6, f7, f3, f0, f0, f1, f0,
	-f2, f3, f4, f5, f3, f6, f0, f1, f0,-f2, f3,-f4,-f2, f3, f4, f0, f1, f0,
	 f5, f3,-f6,-f2, f3,-f4, f0, f1, f0, f7, f3, f0, f5, f3,-f6, f0, f1, f0,
	};
	float mNormals[]={
	 f8,-f9, fa, f8,-f9, fa, f8,-f9, fa, fb,-fc, f0, fb,-fc, f0, fb,-fc, f0,
	-fd,-fc, fe,-fd,-fc, fe,-fd,-fc, fe,-fd,-fc,-fe,-fd,-fc,-fe,-fd,-fc,-fe,
	 f8,-f9,-fa, f8,-f9,-fa, f8,-f9,-fa, ff,-f8, f0, ff,-f8, f0, ff,-f8, f0,
	 fg,-fh, fi, fg,-fh, fi, fg,-fh, fi,-fj,-fk, fl,-fj,-fk, fl,-fj,-fk, fl,
	-fj,-fk,-fl,-fj,-fk,-fl,-fj,-fk,-fl, fg,-fh,-fi, fg,-fh,-fi, fg,-fh,-fi,
	 fj, fk, fl, fj, fk, fl, fj, fk, fl,-fg, fh, fi,-fg, fh, fi,-fg, fh, fi,
	-ff, f8, f0,-ff, f8, f0,-ff, f8, f0,-fg, fh,-fi,-fg, fh,-fi,-fg, fh,-fi,
	 fj, fk,-fl, fj, fk,-fl, fj, fk,-fl, fd, fc, fe, fd, fc, fe, fd, fc, fe,
	-f8, f9, fa,-f8, f9, fa,-f8, f9, fa,-fb, fc, f0,-fb, fc, f0,-fb, fc, f0,
	-f8, f9,-fa,-f8, f9,-fa,-f8, f9,-fa, fd, fc,-fe, fd, fc,-fe, fd, fc,-fe,
	};

物体データの、mVerticesとmNormalsをこのコードに差し替えます。
実行結果はこうなります。

icosahedral

あとは、描画の度に物体の位置や角度を変えてやればアニメーションになります。
まず、回転を保持するクラス変数rxを定義します。

	float rx=0;

onDrawFrame()の物体の配置を決めるコードで少しずつ回転を加えます。

	//物体の配置を決める
	GLES11.glRotatef(rx+=1, 1.0f, 0.0f, 0.0f);

僅か2行の変更ですが、このように物体に動きが加わる事で立体らしく見えてきます。

glRotatefは物体を回転させるメソッドですが、平行移動させるglTranslatefという
メソッドもあります。この2つを組み合わせ、空間の好きな位置に物体を配置します。
同じ物体を何個も表示させるには、描画メソッドのglDrawArraysを何度も呼びだすだけです。
ただし、位置はずらしてやらないと重なって一つの物体にしか見えません。
コードはこんな感じになります。

	for (int i=0;i<10;i++) {
		//カメラの設定
		GLES11.glMatrixMode(GLES11.GL_MODELVIEW);
		GLES11.glLoadIdentity();
		GLU.gluLookAt(gl10, 0,5,20, 0,6,0, 0,1,0);

		//物体を配置する
		GLES11.glTranslatef(i,0,0);

		//描画する
		GLES11.glDrawArrays(GLES11.GL_TRIANGLES,0,mVertices.length/3);
	}

同様にループ内でglMaterialfvを呼びだしてやれば、物体毎に色を変える事もできます。
物体を配置する、色を変える、何個も表示させるといった実装を大雑把に説明しましたが、
それらを駆使するだけでもこんなCGを作る事ができます。

3D空間上をリアルタイムに物体が動く、動かせるという事が楽しく面白い。
そこを突破口に徐々に難しい事に挑戦していけば、すんなりと習得できるのではないでしょうか。

色々な環境に対応してるので応用範囲は広いですし、身に付けて損はない気がします。