2011年8月23日火曜日

GL SurfaceView の導入

アプリケーションで android.opengl.GLSurfaceView クラスを以下の方法で使用することにより、簡単にOpenGL ES レンダリングを使用できるようになります。

  • OpenGL ES  View システムに接続するための接着剤となるコードを提供します。
  • OpenGL ES  Activity のライフサイクルと一緒に動作させるための接着剤となるコードを提供します。
  • 適切なバッファピクセルフォーマットの選択が楽になるようにします。
  • 分離したレンダリングスレッドを作成し管理することによりスムーズなアニメーションが可能となります。
  • OpenGL ES API の呼び出しをトラックし、エラーをチェックするための簡便なデバッグツールを提供します。

GLSurfaceView  OpenGL ES を使用して部分的または全体的にレンダリングを行うアプリケーションを構築するための最適な基礎となります。2D 3D のアクションゲームは対象としは最適であり、同じように 2D 3D データの可視化する Google マップのストリートビュー といったアプリケーションもその対象となるでしょう。

簡単な GLSurfaceView アプリケーション

以下は可能な限りシンプルな OpenGL ES アプリケーションのソースコードです。

package com.example.android.apis.graphics;

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

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

public class ClearActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mGLView = new GLSurfaceView(this);
        mGLView.setRenderer(new ClearRenderer());
        setContentView(mGLView);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mGLView.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mGLView.onResume();
    }

    private GLSurfaceView mGLView;
}

class ClearRenderer implements GLSurfaceView.Renderer {
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // Do nothing special.
    }

    public void onSurfaceChanged(GL10 gl, int w, int h) {
        gl.glViewport(0, 0, w, h);
    }

    public void onDrawFrame(GL10 gl) {
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    }
}

このプログラムは十分ではありません。これだと画面のフレームがまっさらになってしまいます。しかしこれでも Android のアクティビティのライフサイクルを正しく実装した完全な OpenGL のアプリケーションです。アクティビティが一時停止するとレンダリングが一時停止され、アクティビティが回復するとレンダリングが回復されます。このアプリケーションを相互作用しないデモ用のプログラムの元として使用できるかもしれません。さらに OpenGL  ClearRenderer.onDrawFrame() メソッドに対する呼び出しを追加するだけです。GLSurfaceView ビューのサブクラスは不要である点に注目してください。

GLSurfaceView.Renderer には以下の 3 つのメソッドがあります。

  • onSurfaceCreated() メソッドはレンダリングの開始時点と、 OpenGL ES 描画コンテキストが再生成さたとき常に呼び出されます ( 通常はアクティビティが一時停止と回復したときに描画コンテキストが失われたり再生成されたりします ) 。テクスチャといった長い間生存している OpenGL リソースを生成するのに OnSurfaceCreated() は最適な場所です。
  • onSurfaceChanged() メソッドはサーフェスがサイズを変更したときに呼び出されます。これは OpenGL ビューポートを設定するのに最適な場所です。また、動き回ったりしない固定のカメラなどはここで設定したくなるでしょう。
  • onDrawFrame() メソッドはすべてのフレームに呼び出され、シーンの描画を担っています。通常は glClear を呼び出してフレームバッファのクリアを行うことから始め、後続で他の OpenGL ES を呼び出して現在のシーンを描画することになります。

ユーザの入力をどう扱うのか?

相互作用するアプリケーション ( ゲームのような ) にしたい場合は、入力のイベントを受け取る手段が容易なことから、通常は GLSurfaceView をサブクラス化します。以下はその方法を示した少し長めの例です。

package com.google.android.ClearTest;

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

import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

public class ClearActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mGLView = new ClearGLSurfaceView(this);
        setContentView(mGLView);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mGLView.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mGLView.onResume();
    }

    private GLSurfaceView mGLView;
}

class ClearGLSurfaceView extends GLSurfaceView {
    public ClearGLSurfaceView(Context context) {
        super(context);
        mRenderer = new ClearRenderer();
        setRenderer(mRenderer);
    }

    public boolean onTouchEvent(final MotionEvent event) {
        queueEvent(new Runnable(){
            public void run() {
                mRenderer.setColor(event.getX() / getWidth(),
                        event.getY() / getHeight(), 1.0f);
            }});
            return true;
        }

        ClearRenderer mRenderer;
}

class ClearRenderer implements GLSurfaceView.Renderer {
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // Do nothing special.
    }

    public void onSurfaceChanged(GL10 gl, int w, int h) {
        gl.glViewport(0, 0, w, h);
    }

    public void onDrawFrame(GL10 gl) {
        gl.glClearColor(mRed, mGreen, mBlue, 1.0f);
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    }

    public void setColor(float r, float g, float b) {
        mRed = r;
        mGreen = g;
        mBlue = b;
    }

    private float mRed;
    private float mGreen;
    private float mBlue;
}

このアプリケーションはすべてのフレームに対して画面をクリアし、タッチイベントの (x,y) 座標を元にクリアする色を設定しています。ClearGLSurfaceView.onTouchEvent().  queueEvent() を使用している点に注目してください。。queueEvent() メソッドは UI スレッドとレンダリングスレッドとの間で安全に通信をする目的で使用されます。好みにより、 Renderer クラス自体に同期化メソッドを設けるなど他のスレッド間通信のテクニックを使用することも可能です。しかしながら、ほとんどの場面でこのイベントキューイングにするのがスレッド間通信を扱う上で最も簡単な方法です。

他の GLSurfaceView サンプル

画面をクリアするだけでは飽きたでしょう? Android SDK API Demo サンプルにはさらに興味を引くサンプルが入っています。以下のすべての OpenGL ES サンプルは GLSurfaceView ビューを使用するように様変わりしました。

  • GLSurfaceView - 回転する三角形です
  • Kube - 立方体パズルのデモです
  • Translucent GLSurfaceView - 半透明の背景で 3D グラフィックスを表示する方法を示しています
  • Textured Triangle - テクスチャ化された 3D 三角形を描画する方法を示しています
  • Sprite Text - テクスチャ内にテキストを描画し、3D シーンにそれを組み入れる方法を示しています
  • Touch Rotate - ユーザインプットに反応して 3D オブジェクトを回転させる方法を示しています

サーフェスの選択

GLSurfaceView はレンダリングするサーフェスのタイプ選択を支援します。異なる Android デバイスは共通のサブセットを持たず、それぞれで異なるタイプのサーフェスをサポートします。このことがそれぞれのデバイスで使用できる最良のサーフェスを選択する上で厄介な問題を引き起こします。

デフォルトでは、GLSurfaceView  16 ビット深度のバッファで 16 ビット RGB フレームバッファにできる限り近いサーフェスを見つけ出すことを試みます。作成するアプリケーションの必要に応じて、その振る舞いを変更したいこともあるかもしれません。例えば、半透明の GLSurfaceView サンプルでは、半透明のデータをレンダリングするために、ひとつのアルファチャネルが必要です。 GLSurfaceView には以下のような setEGLSurfaceChooser() のオーバーロードメソッドがあり、どのサーフェスタイプが選択されるのかを管理する選択肢を提供します。

setEGLConfigChooser(boolean needDepth)

16 ビットのフレームバッファを使用または使用しない R5G6B5 に最も近い設定を選択します。

setEGLConfigChooser(int redSize, int greenSize,int blueSize, int alphaSize,int depthSize, int stencilSize)

最低値がコンストラクタで指定したビット/チャネルと同じビット/ピクセルの値が最も少ない設定を選択します。

setEGLConfigChooser(EGLConfigChooser configChooser)

選択のすべての管理を指定の設定に任せます。 デバイスの機能を検出して選択するような独自の EGLConfigChooser を実装し、それを渡します。

絶え間ないレンダリング  vs その都度のレンダリング

ゲームやシミュレーションなどほとんどの 3D アプリケーションは絶え間なく動いています。しかし中には反応型の 3D アプリケーションもあります。ユーザによるなんらかの動作を受け入れるまで待ち、それに応答するのです。そのようなタイプのアプリケーションでは、画面を絶えず再描画する GLSurfaceView のデフォルトの振る舞いだと時間を浪費することになってしまいます。反応型のアプリケーションを開発している場合は、 GLSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY) を呼び出すことにより、絶え間なくアニメーションを動かすことを無効にすることができます。その後再度レンダリングしたい都度 GLSurfaceView.requestRender() を呼び出します。

デバッグ支援

GLSurfaceView  OpenGL ES アプリケーションのデバッグのための便利な機能を持っています。GLSurfaceView.setDebugFlags() メソッドで OpenGL ES 対する呼び出しのログ出力と ( または ) エラーチェックを有効な状態で使用することができます。GLSurfaceView のコンストラクタで setRenderer() を呼び出す前にこのメソッドを呼び出してください。

public ClearGLSurfaceView(Context context) {
    super(context);
    // Turn on error-checking and logging
    setDebugFlags(DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS);
    mRenderer = new ClearRenderer();
    setRenderer(mRenderer);
}

 

0 件のコメント:

コメントを投稿