ApiDemosを調べる2 - 個別API GLES20Activity
graphics -> OpenGL ES -> OpenGL ES 2.0
で、GLES20Activityを実行します。
public class GLES20Activity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mGLSurfaceView = new GLSurfaceView(this); if (detectOpenGLES20()) { // Tell the surface view we want to create an OpenGL ES 2.0-compatible // context, and set an OpenGL ES 2.0-compatible renderer. mGLSurfaceView.setEGLContextClientVersion(2); mGLSurfaceView.setRenderer(new GLES20TriangleRenderer(this)); } else { // Set an OpenGL ES 1.x-compatible renderer. In a real application // this renderer might approximate the same output as the 2.0 renderer. mGLSurfaceView.setRenderer(new TriangleRenderer(this)); } setContentView(mGLSurfaceView); } private boolean detectOpenGLES20() { ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); ConfigurationInfo info = am.getDeviceConfigurationInfo(); return (info.reqGlEsVersion >= 0x20000); } @Override protected void onResume() { // Ideally a game should implement onResume() and onPause() // to take appropriate action when the activity looses focus super.onResume(); mGLSurfaceView.onResume(); } @Override protected void onPause() { // Ideally a game should implement onResume() and onPause() // to take appropriate action when the activity looses focus super.onPause(); mGLSurfaceView.onPause(); } private GLSurfaceView mGLSurfaceView; }
onPause()とonResume()は割愛します。
(Pauseはほかのアプリにフォーカスが移ったときに呼び出される。
その時の状態を保存したりする。
Resumeはほかのアプリからまた戻ってきたときに呼び出される。
以前保存した状態を呼び出したりする。)
onCreate()が呼ばれる。
次にGLSurfaceViewクラスのインスタンスを生成する。
detectOpenGLES20はシステムがGLES2.0に対応しているかどうかを確認する。
自分はエミュレータ上で実行したところ、非対応になってしまった。
(GLES2.0に対応している場合には、EGLContextのバージョンを2に設定してから)
setRendererでRendererを設定する。
setRendererの内部実装は以下のとおり(Android SDKだけではアクセスできないのでソースが必要)
public void setRenderer(Renderer renderer) { checkRenderThreadState(); if (mEGLConfigChooser == null) { mEGLConfigChooser = new SimpleEGLConfigChooser(true); } if (mEGLContextFactory == null) { mEGLContextFactory = new DefaultContextFactory(); } if (mEGLWindowSurfaceFactory == null) { mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory(); } mRenderer = renderer; mGLThread = new GLThread(renderer); mGLThread.start(); }
checkRenderThreadStateはすでにmGLThreadが生成されていないかを
確認するもの。
その後、GLSurfaceViewクラスに必要なインスタンス等生成していく。
描画用のThreadを別途生成している。
RendererはGLSurfaceViewの中にinterfaceが書かれている。
public interface Renderer { /** * Called when the surface is created or recreated. * */ void onSurfaceCreated(GL10 gl, EGLConfig config); /** * Called when the surface changed size. * @param gl the GL interface. Use <code>instanceof</code> to * test if the interface supports GL11 or higher interfaces. * @param width * @param height */ void onSurfaceChanged(GL10 gl, int width, int height); /** * Called to draw the current frame. * @param gl the GL interface. Use <code>instanceof</code> to * test if the interface supports GL11 or higher interfaces. */ void onDrawFrame(GL10 gl); }
onSurfaceCreatedは起動時に呼ばれる。
GLの初期化をしたり、画面サイズを設定したりする。
今回は、TriangleRenderer.javaの中で以下の記述がなされている。
public void onSurfaceCreated(GL10 gl, EGLConfig config) { gl.glDisable(GL10.GL_DITHER); gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); gl.glClearColor(.5f, .5f, .5f, 1); gl.glShadeModel(GL10.GL_SMOOTH); gl.glEnable(GL10.GL_DEPTH_TEST); gl.glEnable(GL10.GL_TEXTURE_2D); int[] textures = new int[1]; gl.glGenTextures(1, textures, 0); mTextureID = textures[0]; gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE); InputStream is = mContext.getResources() .openRawResource(R.raw.robot); Bitmap bitmap; try { bitmap = BitmapFactory.decodeStream(is); } finally { try { is.close(); } catch(IOException e) { // Ignore. } } GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); }
GLの初期設定と、Textureの準備を行っている。
リソースのR.raw.robotをbitmapにして、それを
GLUtils.texImage2D()でTextureに設定している。
(ちなみに、R.raw.の中にリソースを配置するとDPI(画素密度)によらず、
常にオリジナルの画像を使用してくれるらしい)
onSurfaceChangedは画面サイズが変わった、たとえば回転などのときに呼ばれる。
画面サイズを設定したり、GLの再設定をしたりする。
初回にも上記onSurfaceCreated()の後に呼ばれてくる。
今回は、以下の処理を行っている。
public void onSurfaceChanged(GL10 gl, int w, int h) { gl.glViewport(0, 0, w, h); float ratio = (float) w / h; gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7); }
視点(Viewportと射影行列)に関する設定。
Viewportは画面の幅と高さ。Frustum(視錐台)で見える範囲を設定。
2Dの場合はPerspectiveをOrtho設定とする。遠くも近くも同じ大きさになる。
onDrawFrameが毎回呼ばれる処理。
ここで描画する。
今回は、以下の処理を行っている。
public void onDrawFrame(GL10 gl) { gl.glDisable(GL10.GL_DITHER); gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE); gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glActiveTexture(GL10.GL_TEXTURE0); gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID); gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT); gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT); long time = SystemClock.uptimeMillis() % 4000L; float angle = 0.090f * ((int) time); gl.glRotatef(angle, 0, 0, 1.0f); mTriangle.draw(gl); }
画面をクリアしてからモデルビュー行列に対して設定しています。
特にここでは、システムから時間を取得して回転角度を更新させています。
最後のdrawは以下のとおり。
public void draw(GL10 gl) { gl.glFrontFace(GL10.GL_CCW); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer); gl.glEnable(GL10.GL_TEXTURE_2D); gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTexBuffer); gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, VERTS, GL10.GL_UNSIGNED_SHORT, mIndexBuffer); }
頂点配列(mFVertexBuffer。事前に三角形の座標を格納している)と
それに対応するテクスチャ座標配列(mTexBuffer)を設定してDrawElementsで
描画しています。ここではドロイド君がテクスチャとして貼られた
三角形が表示されます。
これはGLES1.1の場合の処理です。
GLES2.0の場合はシェーダプログラムが必要になります。
onDraw()では描画のみで、画面の更新はしない。
画面の更新はEGLが行う。
GLSurfaceView.javaのなかにEGLHelperクラスがあり、その中に
swap()関数がある。
public boolean swap() { if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { int error = mEgl.eglGetError(); switch(error) { .... } } return true; }
というわけで、ただ単にeglSwapBuffers()を呼び出しているだけです。