Androidアプリ開発 OpenGLで描画する [Android OpenGL]
ええと、前回、OpenGLを使って「真っ黒な画面」を表示することには成功した。しかし、本当に動いているのかどうか「怪しい」。そこで何かを表示させてみたいと思う。
レンダラーのonDrawFrameの中に、描画用のコードを書いていけばよいのだが、けっこういろいろ大変。
OpenGLは2Dでの描画を行うことも可能ではあるが、やはり3Dじゃないと面白くない。
まずは、描画する3次元データを作らなくてはならない。
いきなりガンダムを表示する、というのは無理なので、ごく単純な三角形を描画することにする。OpenGL ESでは、多角形は表示できない。多角形を表示するには、三角形を組み合わせて表示する。ESが付いていない単なるOpenGLには多角形を表示する命令がある。
三角形を3次元データとして数値で表してやらないといけない。2Dの描画でも、こっからここまでの四角を赤で塗りつぶし、といったことを座標値や色の数値、形の数値などで指示する。3Dでも同じ、場所を意味する数値データが3次元(x,y,z)になったり、色の指定の仕方が、単純に赤だけでなく、アンビエント、ディフューズ、スペキュラーなどに細かくなるだけ。
最初は、モデルの表示位置を決定する座標値を作っていく。3次元データは別のクラスを作ってそこで定義した方がよいであろう。クラス「Model」を作成し、コンストラクタでデータを定義することにする。
vertexが3次元での座標値データ。配列でx,y,zの順で座標値が書いてある。三角形なので、x,y,zで示される座標値が3つ存在している。
C言語で書いた場合は、配列をそのままOpenGLの関数に渡すことができるが、androidは基本Javaなので、バイト列に変換してやる必要がある。
それを行ったのが、以下のコード。
ByteBufferは、java.nioパッケージに含まれるクラスで、ネイティブIOに使用するもの。allocateDirectでメモリをダイレクトにアロケーションする(ってそのままの説明だな)。C言語でmallocしたのと同じと思っておけばOKか。
引数には、領域の大きさを指定する。vertex配列の大きさが9個。vertex.lengthは9となるはず。4をかけているのは、float型の大きさである4バイトを計算しているから。allocateDirectに渡す引数の単位は、「バイト」になる。
allocateDirectに成功すると、ByteBufferのインスタンスが戻される。上記のコードでは、変数vbでインスタンスを記憶している。vbはアロケーションされたメモリ領域を持っているが、メモリの中には何も入っていない。ここに座標値をつっこみたいのだが、座標値がfloat型であるため、asFloatBufferでFloatBufferクラスに変換して使用する。
C言語でいったら、「ポインタをキャストした」という感じか。
asFloatBufferでキャストした後のインスタンスをフィールド「buffer」で記憶しておく。
次に、putすると、vertexの内容がFloatBufferの中に収められる。FloatBufferには参照する要素の位置が指定できるようなので、これをposition(0)で先頭に戻しておく。
これで、座標値データの作成はできた。次はこれを「描画」してみよう。
描画用に、drawメソッドを作ることにする。描画にはOpenGLのインスタンスが必要になるので、これを引数で受けるようにする。レンダラーのonDrawFrameからModelのdrawが呼び出されることになるが、GL10 glを引数で渡すことにする。
何がなんやらわけがわからないと思いますが、順を追って説明していきますか。
まず、glEnableClientStateは、描画機能のON/OFFに使われるもの。VERTEX_ARRAYを有効にしている。座標値データ(頂点データ)をbufferに作成したので、これを使って三角形を描画したい。glDrawArraysで描画を行うわけではあるが、その際にVERTEX_ARRAY(頂点配列)を使って描画することの指示になる。
glVertexPointerは、メモリ中の頂点データの場所を描画ルーチンに知らせるためのもの。
これで三角形が表示できるはず。レンダラーに組み込んでみよう。
SampleGLSurfaceViewのフィールドでModelのインスタンスを作っておいて、onDrawFrameからdrawを呼び出してみる。
これで、描画できるはず...
エミュレータを起動して実行してみても、期待は外れるであろう。真っ黒な画面のまま。
ビューポート・フラスタム
どうしてかというと、カメラ位置やビューポート、フラスタムなどいろいろなものを設定していないからなのです。2Dの場合は、画面の座標系はデフォルト設定のままでもなんとなく描画されるが、3Dの場合は、カメラの向いている方向をきっちり設定してやって、なおかつ、その中に物体が存在していないと、何も表示されない。
カメラ位置は、とりあえずデフォルトの位置(0,0,0)にしておき、モデルの方を移動させることにする。
ビューポートは、額縁みたいなもので、onSurfaceChangedで、サーフェースの大きさがやってくるので、ここで設定すればOK。ちょうど端末の画面がスクリーンになって、その向こう側に仮想空間が広がっていると思えばよい。
フラスタムの設定はたくさんパラメータがありやっかいだ。とりあえずは、以下のようにしておけばよいであろう。
ビューポートの設定を、glViewportで行っている。
glMatrixModeやら、glLoadIdentityというのが出てきてなんのことやらわからないが、こういうものであると思って欲しい。カメラ関係のパラメータをいじる際は、GL10.GL_PROJECTIONを指定して、マトリックスを選択してから設定を行うようにする。
glLoadIdentityは、単位マトリックスの読み込み。マトリックスの「初期化」みたいなものである。
フラスタムの設定がgluPerspective。
画角、アスペクト比、クリップポイントの近いところと、遠いところの指定を行っている。
フラスタムの説明は、以下のサイトの説明がわかりやすい。
http://android.keicode.com/basics/opengl-drawing-basic-shapes.php
次に、モデルの移動をする。
視点は、0,0,0にあり、カメラは0,0,-1の方向を向いている(Z軸の負の方向に向いている)。なので、モデルをZ軸でマイナス方向に移動させたい。
3DCGでは、アフィン変換といった行列計算で移動、拡大、回転を行うようになっている。これは、OpenGLだけではなく、Direct3Dやらの他のグラフィックスライブラリでも同じである。難しい原理はよくわからなくても、行列(マトリックス)の計算で移動や回転ができるんだなと覚えておけばOK。
移動はglTranslatef、拡大はglScalef、回転はglRotatefで行うことができる。
でもって、移動をするには、glTranslateを使えばよいのだが、モデルの座標値を変換したいので、モデルの座標を変換するマトリックスに対して、値を設定しなければならない。onSurfaceChangedで、フラスタムを設定するために、変換マトリックスにはGL_PROJECTIONが選択されている。なので、そのままだとカメラ関係の座標変換が行われてしまうので、GL_MODELVIEWを選択してやる必要がある。
さらに、単位マトリックスに初期化した上で、glTranslatefで移動をかけるようにする。後は、drawで描画すればOK。
これらの一連の処理をonDrawFrameで行うように変更する。
ここまでできたら、エミュレータで(もちろん実機でもかまわない)動かしてみて欲しい。三角形が表示されるはず。
今回はここまで。
サイト内を検索
レンダラーのonDrawFrameの中に、描画用のコードを書いていけばよいのだが、けっこういろいろ大変。
OpenGLは2Dでの描画を行うことも可能ではあるが、やはり3Dじゃないと面白くない。
まずは、描画する3次元データを作らなくてはならない。
いきなりガンダムを表示する、というのは無理なので、ごく単純な三角形を描画することにする。OpenGL ESでは、多角形は表示できない。多角形を表示するには、三角形を組み合わせて表示する。ESが付いていない単なるOpenGLには多角形を表示する命令がある。
三角形を3次元データとして数値で表してやらないといけない。2Dの描画でも、こっからここまでの四角を赤で塗りつぶし、といったことを座標値や色の数値、形の数値などで指示する。3Dでも同じ、場所を意味する数値データが3次元(x,y,z)になったり、色の指定の仕方が、単純に赤だけでなく、アンビエント、ディフューズ、スペキュラーなどに細かくなるだけ。
最初は、モデルの表示位置を決定する座標値を作っていく。3次元データは別のクラスを作ってそこで定義した方がよいであろう。クラス「Model」を作成し、コンストラクタでデータを定義することにする。
public class Model { public Model() { float vertex[] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, }; } }
vertexが3次元での座標値データ。配列でx,y,zの順で座標値が書いてある。三角形なので、x,y,zで示される座標値が3つ存在している。
C言語で書いた場合は、配列をそのままOpenGLの関数に渡すことができるが、androidは基本Javaなので、バイト列に変換してやる必要がある。
それを行ったのが、以下のコード。
import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; public class Model { private FloatBuffer buffer; public Model() { float vertex[] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, }; ByteBuffer vb = ByteBuffer.allocateDirect(vertex.length * 4); vb.order(ByteOrder.nativeOrder()); buffer = vb.asFloatBuffer(); buffer.put(vertex); buffer.position(0); } }
ByteBufferは、java.nioパッケージに含まれるクラスで、ネイティブIOに使用するもの。allocateDirectでメモリをダイレクトにアロケーションする(ってそのままの説明だな)。C言語でmallocしたのと同じと思っておけばOKか。
引数には、領域の大きさを指定する。vertex配列の大きさが9個。vertex.lengthは9となるはず。4をかけているのは、float型の大きさである4バイトを計算しているから。allocateDirectに渡す引数の単位は、「バイト」になる。
allocateDirectに成功すると、ByteBufferのインスタンスが戻される。上記のコードでは、変数vbでインスタンスを記憶している。vbはアロケーションされたメモリ領域を持っているが、メモリの中には何も入っていない。ここに座標値をつっこみたいのだが、座標値がfloat型であるため、asFloatBufferでFloatBufferクラスに変換して使用する。
C言語でいったら、「ポインタをキャストした」という感じか。
asFloatBufferでキャストした後のインスタンスをフィールド「buffer」で記憶しておく。
次に、putすると、vertexの内容がFloatBufferの中に収められる。FloatBufferには参照する要素の位置が指定できるようなので、これをposition(0)で先頭に戻しておく。
これで、座標値データの作成はできた。次はこれを「描画」してみよう。
描画用に、drawメソッドを作ることにする。描画にはOpenGLのインスタンスが必要になるので、これを引数で受けるようにする。レンダラーのonDrawFrameからModelのdrawが呼び出されることになるが、GL10 glを引数で渡すことにする。
public void draw(GL10 gl) { gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, buffer); gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3); }
何がなんやらわけがわからないと思いますが、順を追って説明していきますか。
まず、glEnableClientStateは、描画機能のON/OFFに使われるもの。VERTEX_ARRAYを有効にしている。座標値データ(頂点データ)をbufferに作成したので、これを使って三角形を描画したい。glDrawArraysで描画を行うわけではあるが、その際にVERTEX_ARRAY(頂点配列)を使って描画することの指示になる。
glVertexPointerは、メモリ中の頂点データの場所を描画ルーチンに知らせるためのもの。
これで三角形が表示できるはず。レンダラーに組み込んでみよう。
public class SampleGLSurfaceView extends GLSurfaceView { private Model model = new Model(); // レンダラークラス class OpenGLRenderer implements Renderer { @Override public void onDrawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT); model.draw(gl); }
SampleGLSurfaceViewのフィールドでModelのインスタンスを作っておいて、onDrawFrameからdrawを呼び出してみる。
これで、描画できるはず...
エミュレータを起動して実行してみても、期待は外れるであろう。真っ黒な画面のまま。
ビューポート・フラスタム
どうしてかというと、カメラ位置やビューポート、フラスタムなどいろいろなものを設定していないからなのです。2Dの場合は、画面の座標系はデフォルト設定のままでもなんとなく描画されるが、3Dの場合は、カメラの向いている方向をきっちり設定してやって、なおかつ、その中に物体が存在していないと、何も表示されない。
カメラ位置は、とりあえずデフォルトの位置(0,0,0)にしておき、モデルの方を移動させることにする。
ビューポートは、額縁みたいなもので、onSurfaceChangedで、サーフェースの大きさがやってくるので、ここで設定すればOK。ちょうど端末の画面がスクリーンになって、その向こう側に仮想空間が広がっていると思えばよい。
フラスタムの設定はたくさんパラメータがありやっかいだ。とりあえずは、以下のようにしておけばよいであろう。
@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); }
ビューポートの設定を、glViewportで行っている。
glMatrixModeやら、glLoadIdentityというのが出てきてなんのことやらわからないが、こういうものであると思って欲しい。カメラ関係のパラメータをいじる際は、GL10.GL_PROJECTIONを指定して、マトリックスを選択してから設定を行うようにする。
glLoadIdentityは、単位マトリックスの読み込み。マトリックスの「初期化」みたいなものである。
フラスタムの設定がgluPerspective。
画角、アスペクト比、クリップポイントの近いところと、遠いところの指定を行っている。
フラスタムの説明は、以下のサイトの説明がわかりやすい。
http://android.keicode.com/basics/opengl-drawing-basic-shapes.php
次に、モデルの移動をする。
視点は、0,0,0にあり、カメラは0,0,-1の方向を向いている(Z軸の負の方向に向いている)。なので、モデルをZ軸でマイナス方向に移動させたい。
3DCGでは、アフィン変換といった行列計算で移動、拡大、回転を行うようになっている。これは、OpenGLだけではなく、Direct3Dやらの他のグラフィックスライブラリでも同じである。難しい原理はよくわからなくても、行列(マトリックス)の計算で移動や回転ができるんだなと覚えておけばOK。
移動はglTranslatef、拡大はglScalef、回転はglRotatefで行うことができる。
でもって、移動をするには、glTranslateを使えばよいのだが、モデルの座標値を変換したいので、モデルの座標を変換するマトリックスに対して、値を設定しなければならない。onSurfaceChangedで、フラスタムを設定するために、変換マトリックスにはGL_PROJECTIONが選択されている。なので、そのままだとカメラ関係の座標変換が行われてしまうので、GL_MODELVIEWを選択してやる必要がある。
さらに、単位マトリックスに初期化した上で、glTranslatefで移動をかけるようにする。後は、drawで描画すればOK。
これらの一連の処理をonDrawFrameで行うように変更する。
@Override public void onDrawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); gl.glTranslatef(0, 0, -3f); model.draw(gl); }
ここまでできたら、エミュレータで(もちろん実機でもかまわない)動かしてみて欲しい。三角形が表示されるはず。
今回はここまで。
サイト内を検索
2012-04-06 10:06
nice!(0)
コメント(0)
Copyright Atsushi Asai Google+朝井淳
[データベースの気持ちがわかる]SQLはじめの一歩 (WEB+DB PRESS plus)
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2015/03/03
- メディア: 単行本(ソフトカバー)
Access クエリ 徹底活用ガイド ~仕事の現場で即使える
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2018/05/25
- メディア: 大型本
コメント 0