Android / Java と OpenGL ES 2.0 の要点

以下のような人(要するに僕自身のケースと同じ)を対象にしている:

  • Android / Java のアプリ開発についての一般的な理解はある
  • ImageView.onDraw や SurfaceView による動画ならそれほど苦労せずに理解できた
  • しかし、OpenGL (ES 2.0) のことになると色んな解説やサンプルコードを見ても理解に苦しんで中々はかどらない

なぜ OpenGL はわざわざああいうことをするようになっているのか?

ImageView.onDraw や SurfaceView による動的グラフィックスなら理解しやすかった人にとって、動画とは、パラパラ漫画のようなものというほとんど無意識的な大前提があるわけだ。パラパラ漫画に限らず、映画やアニメなど、世の中の映像記録方式としては、こちらが圧倒的主流。ImageView.onDraw や SurfaceView の場合も、ループを回して、フレームバッファに次々とコマを描き込むだけ。

ところが、OpenGL はそれを許してくれないので、何か物凄く高度な、GPU のハードウェア的な性能を引き出すために、OpenGL の API を通じて描画する必要があるのかと、勘違いする。実際はそう(高度な GPU のハードウェア的な性能を引き出した上でレンダリングするのが目的の API 体系)ではないので、架空の妄想を相手に戦う羽目になり、決して勝利することができない戦いに挑む形になる。

OpenGL はそもそも、パラパラ漫画的なことをプログラマーが自らやることを想定していない(GPU 側の内部処理として行われる)。その一歩手前の部分(モデリング)を主にするのがプログラマーの仕事。モデルを基にしてラスタライズしてフレームバッファに落し込む処理は、GPU 側が(プログラマーが予め定義したフラグメントシェーダーに従って)内部的に行う形になっている。つまり、プログラマーがすべき仕事の領域がそもそも異っている点に気付かなければならない。

言ってみれば、フレームバッファ描き込みによるパラパラ漫画的動画はチカチカと瞬く GIF アニメみたいなもので、OpenGL はヌルヌル動く Flash アニメーションみたいなもの(Flash ももう過去の遺物だが……)。他にも、ペイント系グラフィック(ビットマップ画像、JPEG、PNG、Adobe Photoshop、GIMP)とドロー系グラフィック(ベクター画像、SVG、Adobe Illustrator、Inkscape)の違いと言えば話は早いだろうか。

ビットマップを定義ベクターモデルを定義
フレームバッファ書き込みOpenGL
GIF アニメFlash アニメ
JPEG、PNGSVG
Adobe PhotoshopAdobe Illustrator
GIMPInkscape

ドロー系グラフィックを考えると簡単

そこで、Inkscape(Adobe Illustrator でも同じようなものだろう)を考えてみよう。使ったことのない人は簡単な三角形のオブジェクトを作成して、輪郭線に色を塗り、さらに、内側に色を塗る程度の操作をしてみるといい(その程度のことをするためのチュートリアルは Web 上にいくらでも見つかるだろう)。

OpenGL でプログラマーが API を通じて GPU に与えなければならない情報は、まさしくそれ。ベクターモデルに関する情報を与えているのである。オブジェクトの形そのものは、頂点で定義できる。ただし、これではオブジェクトのモデルだけであり、結果としての画像出力は空白のままである。頂点を結んだ辺(Inkscape ではストロークと呼ばれている)の太さや色、さらに辺で囲まれたポリゴンの面の色やテクスチャー(Inkscape ではフィルと呼ばれている)を定義することで、ラスタライズされた最終出力としてのビットマップがフレームバッファに描き込まれるわけである。

シェーダーとは

ベクターモデルを立体空間の中に配置することによって、画面のどの位置にあるのかを決定するのが、バーテックスシェーダー。配置されたベクターモデルに対し、ストロークやフィルをどのようにするかを決定するのが、フラグメントシェーダーである。

このように、ドロー系のベクターグラフィックスアプリを想定すると、OpenGL がなぜあのような API 構成になっているのかが、明快に理解できるのではないだろうか。

Java 固有の問題

さらにもう一つ、OpenGL そのものではなく、Java 固有の問題というか、OpenGL 利用時に配慮しなければならないポイントがある。それがダイレクトバッファ(ByteBuffer)である。Java は本来 JavaVM で処理することを念頭に置いているので、メモリーも JavaVM 内のヒープ領域を使うことがデフォルトである。これでは、GPU とのデータのやりとりに効率が悪いので、Java で OpenGL を使う場合はダイレクトバッファを通じてデータを転送する。ByteBuffer を使った特有の処理の記述があるのはそれが理由であると考えておけばいい。

コメント

このブログの人気の投稿

OpenWrt での Wi-Fi 設定

清水俊史『上座部仏教における聖典論の研究』

シークエンスパパとも 本物の霊能力