2011年6月16日木曜日

AndroidでOpenGL ES 2.0を学びたい

Androidでゲームを作りたい人なのでGLES20を学んでいます。
OpenGLの知識は1.1で止ったままなので、もはや別物と化してしまったGLES20は改めて学ぶ必要があるようです。
まず、GLES20のAPIを学ぶ上で罠っぽいところが結構ありまして…
  1. OpenGLはステートを内部に溜め込む
  2. ステートによって関数の意味が変わる
  3. 固定シェーダが無くなった
1はオブジェクト指向的に考えると巨大な一枚岩OpenGLオブジェクトが存在するイメージです。明示的に指定しない限り、勝手にステートを変更したりすることはありません。例えばZバッファの比較が不要な場所でOFFにしたあと、ONにするのを忘れると、Zバッファがオフのまま描画され続けることになります。
ならこまめにOnOff切り替えればいいじゃないかと思われる方もいらっしゃるでしょうが、パフォーマンス上の理由であまりお勧めできません。
GPUへの命令発行は可能な限り減らさなければならないのです。

問題は2です。慣れてないと非常に気持ち悪いです。例えばテクスチャアドレッシングモードを変更する(glTexParameteri)とき、対象となるテクスチャをバインド(glBindTexture)しないと変更できません。テクスチャネームを指定して直接変更…できたらいいんですけど、できません。
もうひとつ例を挙げると、Shaderに渡すAttribute変数を設定する関数(glVertexAttribPointer)があるのですが、この関数、VBOがバインドされている場合とそうでない場合では渡す引数の意味が変わります。通常はデータへのポインタを渡すのですが、VBOがバインドされている場合、VBOのオフセットを渡す必要があります。このあたり理解して無いと…それは後述します。

3はまぁ、時代の流れですね。固定シェーダがやってたことと同じシェーダを書くことはそんなに難しいものではないので、特に問題はないと思います。

あと、Androidの開発言語はJavaなわけですが、Javaオンリーで開発すると罠がいくつかあります。
  1. Android2.2のGLES20のAPI設計したヤツでてこい!><
  2. テクスチャ読み込みが回りくどい
まず1です。VBO設定関連のAPIが軒並み使用不可能です。先に説明したとおり、VBOをバインドすると設定関数の意味が変わるわけですが、たぶんそのことを知らなかったのでしょう、2.2ではVBOのオフセット指定することができません。なのでAndroid2.2ではVBOは作成できるけど使用できないというなんともぐんにょりなAPIに仕上がってます。ちなみに2.3では追加されてます。

もうひとつ、OpenGLにはデータチャンクを渡すAPIがあるのですが、これが大抵引数がjava.nio.Bufferなんです。それだけならまだ救いがあるのですが、DirectBufferかつNativeOrderという条件が加わります。
悪いことに、DirectBufferが必要となると、まずByteBufferが必要になるのですが、asXXXBuffer()による変換後のオブジェクトは、array()メソッドがサポートされていません。putで書き込む必要があります。
すなわち、頂点データを加工するには、Javaだと二倍のメモリ(そしてコピーのコスト)を払う必要があるわけです。かといって、メモリをけちって1要素ずつput()したら動作速度は目も当てられないことにになります。

2はもう、あからさまに回りくどいです。手順としては
  1. BitmapFactoryからリソースを読み込み、Bitmapを取得
  2. getPixels()でピクセル値を取得
  3. 色の並びをOpenGLに合わせる(Option)
  4. ネイティブオーダーかつダイレクトなByteBufferを作成(ByteBuffer.allocateDirect() ByteBuffer.order())する。
  5. ByteBufferにピクセル値を書き込む
  6. ビデオメモリに転送(glTexImage(), glTexSubImage())
  7. Bitmapを開放(recycle())
めんどくさい&メモリ食う(画像の3倍のメモリ)&遅い(ピクセル操作)と三拍子そろってます。
なのでNDKを使わざるを得ないのが実情かと。

追記:
Bitmapを直接読み込めるAPIありました…android.opengl.GLUtilsがそれです。
2.3が出回れば、JavaのみでOpenGLを問題なく叩けそうですね。

0 件のコメント:

コメントを投稿