Androidアプリ開発 OpenGL 視点をスクロール(スライド)で操作する [Android OpenGL]
みなさん、こんにちは、本日もAndroidアプリを作っていこうと思います。
視点位置をgluLookAtで変更することができた。端末をタップすることでこれを動的に変えていこうと思う。OpenGLというかandroidの方のネタになる。
OpenGLは、iPhoneやPCでも使用できる。プラットフォームを選ばない、まさしくオープンなものである。なので、androidでの決まりは、OpenGLには持ち込むことはできない。
「モデルを作ったからクリックとかタップは、そっちでなんとかして」
というのは「まかりとおらない」話なのである。
そういった、感じなので、タップなどのイベントは、androidの世界で処理してやらなければならない。アクティビティでonTouchをオーバーライドすればタップした位置は取れるが、ドラッグ(スライド)はゼスチャーっていうのを使ってイベントを別途処理してやる必要がある。
androidは日々進化している。めざましい進化なのではあるが、すぐに「レガシー(時代遅れ)」になってしまう。まぁ、嘆いてもしょうがないことではある。そのうち程良く「枯れる」ことになるであろう。
「早くコードがみたいなぁ」
そうでした。まずは、アクティビティでゼスチャー検出機を作る。
ゼスチャー検出機といっても、ハードウェアではなくSDK中のクラスで提供されているもの。イベントを拾ったら、これをゼスチャーに処理してもらう。ゼスチャーが、イベントを処理して、これはダブルタップだな、とか時間が開いているからロングタップにしよう、といった処理が行われて、リスナーのonLongPressが呼び出される、といった具合。
久しぶりにアクティビティのコードに登場してもらおう。
GestureDetector gesDetectorがゼスチャー検出機。コンストラクタで作成している。GestureDetectorのコンストラクタには引数がふたつ必要。最初の引数は、Context。アクティビティを入れればOK。ふたつ目の引数は、イベントを受けるためのリスナー。OpenGLのビューで受けたいのでviewを指定する。
onTouchEventをオーバーライドしてイベントを一度アクティビティで受けるようにする。イベントがどんどん上がってくるので、そのままゼスチャー検出機にかけてしまう。戻り値もそのまま返してしまう。
アクティビティはこれでOK。SampleGLSurfaceViewの方にイベント処理部分を作っていく。GestureDetector(this, view)のところがviewがリスナーになっていないので、エラーになっているはず。クイックフィックスを使って、SampleGLSurfaceViewクラスにインターフェース「OnGestureListener」の実装を指示する。
今度は、SampleGLSurfaceViewにメソッドが足りないエラーになるので、必要なメソッドをクイックフィックスで作成する。
とりあえず、ドラッグ(スライド)したときに視点を変えたいので、onScrollのみ内容を書く。後はほっておく。
引数の名前も変えている。onScrollには4つの引数でイベントが渡ってくる。詳細はSDKのマニュアルを参照して欲しい。移動量がdistxとdistyでわかるので、視点位置も移動させている。
eyeposは、クラスのフィールドとして定義した。floatの配列で、x,y,z座標の値を記憶する。
初期値は、レンダラーのonSurfaceCreatedで設定するようにした。
おっと、カメラ位置もeyeposにしなくては。
これで、スライドさせれば、視点が動くはず...
しかし、期待外れに終わってしまった。
いくらスライドさせても一向に変化しない。何か間違えたか...
イベントは来ている。eyeposの値も変化している。いろいろ調べった結果、gluLookAtの場所がまずいことが判明。glMatrixMode(GL_MODELVIEW)の後でやるとうまく動いた。
PROJECTIONを選択した後で、やらないといけないことはわかっていたのだが... どっちが正解なんだろうか。
とりあえず動いたので、このままでいくことにする。以下の動画は画面上でスライドさせて三角形を動かしている。三角形のローテーションは止めてある。screen castによる動画キャプチャなので、指が映っていないので、なんか実感がないが... スマホのカメラで動画撮影もやってはみたものの、やっぱりきれいじゃない。これで、かんべんして。
念のため、SampleGLSurfaceViewの全体も貼っておく。
サイト内を検索
視点位置をgluLookAtで変更することができた。端末をタップすることでこれを動的に変えていこうと思う。OpenGLというかandroidの方のネタになる。
OpenGLは、iPhoneやPCでも使用できる。プラットフォームを選ばない、まさしくオープンなものである。なので、androidでの決まりは、OpenGLには持ち込むことはできない。
「モデルを作ったからクリックとかタップは、そっちでなんとかして」
というのは「まかりとおらない」話なのである。
そういった、感じなので、タップなどのイベントは、androidの世界で処理してやらなければならない。アクティビティでonTouchをオーバーライドすればタップした位置は取れるが、ドラッグ(スライド)はゼスチャーっていうのを使ってイベントを別途処理してやる必要がある。
androidは日々進化している。めざましい進化なのではあるが、すぐに「レガシー(時代遅れ)」になってしまう。まぁ、嘆いてもしょうがないことではある。そのうち程良く「枯れる」ことになるであろう。
「早くコードがみたいなぁ」
そうでした。まずは、アクティビティでゼスチャー検出機を作る。
ゼスチャー検出機といっても、ハードウェアではなくSDK中のクラスで提供されているもの。イベントを拾ったら、これをゼスチャーに処理してもらう。ゼスチャーが、イベントを処理して、これはダブルタップだな、とか時間が開いているからロングタップにしよう、といった処理が行われて、リスナーのonLongPressが呼び出される、といった具合。
久しぶりにアクティビティのコードに登場してもらおう。
public class TestOpenGLActivity extends Activity { private GestureDetector gesDetector = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.main); // OpenGL用のビューを作る SampleGLSurfaceView view = new SampleGLSurfaceView(this); // アクティビティにビューを設定 setContentView(view); // ゼスチャー gesDetector = new GestureDetector(this, view); } @Override public boolean onTouchEvent(MotionEvent event){ return gesDetector.onTouchEvent(event); } }
GestureDetector gesDetectorがゼスチャー検出機。コンストラクタで作成している。GestureDetectorのコンストラクタには引数がふたつ必要。最初の引数は、Context。アクティビティを入れればOK。ふたつ目の引数は、イベントを受けるためのリスナー。OpenGLのビューで受けたいのでviewを指定する。
onTouchEventをオーバーライドしてイベントを一度アクティビティで受けるようにする。イベントがどんどん上がってくるので、そのままゼスチャー検出機にかけてしまう。戻り値もそのまま返してしまう。
アクティビティはこれでOK。SampleGLSurfaceViewの方にイベント処理部分を作っていく。GestureDetector(this, view)のところがviewがリスナーになっていないので、エラーになっているはず。クイックフィックスを使って、SampleGLSurfaceViewクラスにインターフェース「OnGestureListener」の実装を指示する。
public class SampleGLSurfaceView extends GLSurfaceView implements OnGestureListener {
今度は、SampleGLSurfaceViewにメソッドが足りないエラーになるので、必要なメソッドをクイックフィックスで作成する。
とりあえず、ドラッグ(スライド)したときに視点を変えたいので、onScrollのみ内容を書く。後はほっておく。
@Override public boolean onScroll(MotionEvent event1, MotionEvent event2, float distx, float disty) { eyepos[0] += distx * 0.01; eyepos[1] += disty * 0.01; return true; }
引数の名前も変えている。onScrollには4つの引数でイベントが渡ってくる。詳細はSDKのマニュアルを参照して欲しい。移動量がdistxとdistyでわかるので、視点位置も移動させている。
public class SampleGLSurfaceView extends GLSurfaceView implements OnGestureListener { private Model model = new Model(); private float angle = 0.0f; private float eyepos[] = new float[3];
eyeposは、クラスのフィールドとして定義した。floatの配列で、x,y,z座標の値を記憶する。
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); gl.glClearDepthf(1.0f); eyepos[0] = 0; eyepos[1] = 0; eyepos[2] = 3; }
初期値は、レンダラーのonSurfaceCreatedで設定するようにした。
おっと、カメラ位置もeyeposにしなくては。
@Override public void onDrawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // カメラ位置を設定 GLU.gluLookAt(gl, eyepos[0], eyepos[1], eyepos[2], 0, 0, 0, 0, 1, 0);
これで、スライドさせれば、視点が動くはず...
しかし、期待外れに終わってしまった。
いくらスライドさせても一向に変化しない。何か間違えたか...
イベントは来ている。eyeposの値も変化している。いろいろ調べった結果、gluLookAtの場所がまずいことが判明。glMatrixMode(GL_MODELVIEW)の後でやるとうまく動いた。
PROJECTIONを選択した後で、やらないといけないことはわかっていたのだが... どっちが正解なんだろうか。
とりあえず動いたので、このままでいくことにする。以下の動画は画面上でスライドさせて三角形を動かしている。三角形のローテーションは止めてある。screen castによる動画キャプチャなので、指が映っていないので、なんか実感がないが... スマホのカメラで動画撮影もやってはみたものの、やっぱりきれいじゃない。これで、かんべんして。
念のため、SampleGLSurfaceViewの全体も貼っておく。
public class SampleGLSurfaceView extends GLSurfaceView implements OnGestureListener { private Model model = new Model(); private float angle = 0.0f; private float eyepos[] = new float[3]; // レンダラークラス class OpenGLRenderer implements Renderer { float lightpos[] = {0.0f, 0.0f, 4.0f, 0.0f}; float red[] = {1.0f, 0.0f, 0.0f, 1.0f}; float green[] = {0.0f, 1.0f, 0.0f, 1.0f}; float blue[] = {0.0f, 0.0f, 1.0f, 1.0f}; float white[] = {1.0f, 1.0f, 1.0f, 1.0f}; float gray[] = {0.5f, 0.5f, 0.5f, 1.0f}; float yellow[] = {1.0f, 1.0f, 0.0f, 1.0f}; @Override public void onDrawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // ライティングをON gl.glEnable(GL10.GL_LIGHTING); // 光源を有効にして位置を設定 gl.glEnable(GL10.GL_LIGHT0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightpos, 0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, red, 0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, blue, 0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, yellow, 0); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); // カメラ位置を設定 GLU.gluLookAt(gl, eyepos[0], eyepos[1], eyepos[2], 0, 0, 0, 0, 1, 0); gl.glPushMatrix(); // マトリックス記憶 gl.glTranslatef(1, 0, 0); gl.glRotatef(angle, 0, 1, 0); gl.glEnable(GL10.GL_CULL_FACE); gl.glCullFace(GL10.GL_BACK); // デプステスト gl.glEnable(GL10.GL_DEPTH_TEST); gl.glDepthFunc(GL10.GL_LEQUAL); gl.glDepthMask(true); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, gray, 0); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, gray, 0); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, gray, 0); gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 80f); gl.glShadeModel(GL10.GL_FLAT); model.draw(gl); gl.glPopMatrix(); // マトリックスを戻す // ふたつめの描画 gl.glPushMatrix(); // マトリックス記憶 gl.glTranslatef(-1, 0, 0); gl.glRotatef(angle, 0, 1, 0); gl.glCullFace(GL10.GL_BACK); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, gray, 0); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, gray, 0); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, gray, 0); gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 80f); // スムースシェーディング gl.glShadeModel(GL10.GL_SMOOTH); model.draw(gl); gl.glPopMatrix(); // マトリックスを戻す //angle += 0.5; // 回転角度は最後に計算 } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { gl.glViewport(0, 0, width, height); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); GLU.gluPerspective(gl, 50f, (float)width / height, 0.01f, 100f); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); gl.glClearDepthf(1.0f); eyepos[0] = 0; eyepos[1] = 0; eyepos[2] = 3; } } private OpenGLRenderer renderer; // サーフェースビューのコンストラクタ public SampleGLSurfaceView(Context context) { super(context); setEGLConfigChooser(8, 8, 8, 8, 16, 0); renderer = new OpenGLRenderer(); setRenderer(renderer); } @Override public boolean onDown(MotionEvent arg0) { // TODO 自動生成されたメソッド・スタブ return false; } @Override public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { // TODO 自動生成されたメソッド・スタブ return false; } @Override public void onLongPress(MotionEvent arg0) { // TODO 自動生成されたメソッド・スタブ } @Override public boolean onScroll(MotionEvent event1, MotionEvent event2, float distx, float disty) { eyepos[0] += distx * 0.01; eyepos[1] += disty * 0.01; return true; } @Override public void onShowPress(MotionEvent arg0) { // TODO 自動生成されたメソッド・スタブ } @Override public boolean onSingleTapUp(MotionEvent arg0) { // TODO 自動生成されたメソッド・スタブ return false; } }
サイト内を検索
2012-04-12 09:26
nice!(0)
コメント(0)
Copyright Atsushi Asai Google+朝井淳
[データベースの気持ちがわかる]SQLはじめの一歩 (WEB+DB PRESS plus)
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2015/03/03
- メディア: 単行本(ソフトカバー)
Access クエリ 徹底活用ガイド ~仕事の現場で即使える
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2018/05/25
- メディア: 大型本
コメント 0