2012年3月25日日曜日

All about OpenGL ES 2.x 1/3

Open GL の「ESの」詳しいドキュメントがあったので、読みがてらのメモです。
元ネタ:bd-interactively All about OpenGL ES 2.x – (part 1/3)
> part 2/3

原文とあわせてお読みください。和訳の正確性と内容の信憑性については話半分ということで。

追記:
この記事を書いたあとで元ネタサイトのコメント欄で、和訳の要約を自分のサイトで公開していい?と質問したところ。
Sure I liked it.
とお返事をいただきました。感謝ですね。

At a glance


OpenGL とは
  • さまざまなプラットフォーム、OSで使用できるグラフィックライブラリ
  • windows, mac, Linux
  • c, c++, Java, Perl, Javascript etc.
  • OpenGLES2.0 はPlaystation, Android, Nokia などに使われている。

著者は2004年の OpenGL2.0 のリリースで感激したらしい。 DirectX は嫌い。


3D world


3次元空間の視覚化とは視覚の再現である。perpectives*、消失点、歪み、深度、焦点、field of view。field of view、単眼/複眼、凹型/凸型レンズなどを理解すると良い。
* perspectives の間違い?

3Dとは3つの次元をもつから3Dである。うわ、何をする、落ち着け。ここで重要なのは「2Dに対して+1の次元を持つ」ということ。

例えば平面上で物体を45度回転するのは簡単。しかし3次元空間ではX軸回転してもいいし、Y軸でも斜めでも良い。当然、軸によって回転後の状態は異なることになる。

つまり次元が「+1」されたということによってかなり複雑になるってこと。

それから「時間」という概念を加えて考えると、3Dは4Dにもなりうる。


OpenGL into the 3D world


OpenGL がどのように動いているか、ということを説明する。

まずは港にあるクレーンを思い浮かべよう(元ネタサイトの写真をみるべし)。港にはコンテナがたくさんあり、なかには crate(木箱?)が入っている。
  • 港全体が OpenGL
  • コンテナが OpenGL オブジェクト (= Textures, Shaders, Meshes etc)
  • コンテナ内の crate は OpenGL を使って僕らが作ったもの (= our instance)
  • ポートクレーンは僕らが使える OpenGL の API

僕らが OpenGL のメソッドを呼び出すのはクレーンに指令をあたえるようなもの。
  • クレーンがコンテナをつかみ、
  • 持ち上げ、
  • そのままコンテナの中でいろんな処理をおこなう。
  • 処理が終わったらコンテナは港のどこかにまた下ろされる。

僕らは港には直接アクセスすることはできないし、コンテナの中身を変えることもできない。できるのはクレーンに指令を出すことだけ

不便なだけに見えるが、そんなことは無い。クレーンは強力で1秒間に何千何万回の指令をこなすことができる。また、 State Machine pattern で設計されているので僕らは余計な情報をもっておく必要がない(コンテナとして預けている)。IDを1つ持っておけば(クレーンの例で言うとコンテナを区別できれば)良いので楽チンである。


How OpenGL works


コンピューターには CPU と GPU がある。CPUはコンピューターや機器の計算処理装置であり、GPU (Graphics Processing Unit)はコンピュータや機器のグラフィックカードである。画像処理は大変なので、GPU がこれを担当していて、おかげで CPU の負担は軽減されている。

CPUに比べて GPU は浮動小数点の扱いにたけている。グラフィックカードの良し悪しで3Dゲームの動きが変わるのはこのため。

といってもOpenGL が完全に GPU の上に乗っかっているわけではなく、モノとしてはハードとは別物。OpenGL が使えるかどうかはそのグラフィックカードが OpenGL に対応しているかによる。


OpenGL’s Logic


プロの3Dソフトウェアは OpenGL でもって超スーパーウルトラ複雑奇妙奇天烈である。その OpenGL のロジックが処理できるのはいくつかのこと。
  • Primitives
  • バッファ
  • ラスタライズ
 マジ?3つだけ?

そう。ではこれらについて1つ1つみていこう。ちなみに2DだとしてもZ深度をゼロとした3Dで扱うことが可能。

・Primitives(和訳:原始的な、基本の)


プリミティブとは基本の形のこと。以下の3つをつかうことができる。
  • 3D Point:3次元上の1点 (x, y, z)
    → およびパーティクルに使用される。
  • 3D Line:1本の線(3D Point x2個で構成される)
    → 3D Vector に利用される。
  • 3D Triangle:1個の三角形(3D Point x3個で構成される)
    → メッシュ(≒立体の物体)の1面として利用される。
OpenGL のバージョンによっては四角形も使えるが、OpenGL ES ではパフォーマンスのため使えない。

・バッファ


簡単に言うと「一時的で最適なデータ倉庫」。OpenGL は3種のバッファをもつ。
  • Frame Buffer
  • Render Buffer
  • Buffer Object

レンダリングの時に、僕らは最終的なイメージをディスプレイに直接送ることもできるし、Frame Buffer に送ることもできる。では Frame Buffer はただの保管庫かというとそうではない。

最終的なイメージは1つのデータから作られるのではないことがほとんど。3Dオブジェクトならオブジェクトの深度もあるし、交差もしているかもしれない。Frame Buffer は画像の集合のように、頂点ごとのデータを保持している。

Render Buffer は一時的な1イメージのデータ倉庫。Frame Buffer は Render Buffer の集合である。Render Buffer にはいくつかの種類がある。
  • Color Render Buffer:最終的な「色」を表すイメージ
  • Depth Render Buffer:最終的なZ方向の深度(奥行き)の情報
  • Stencil Render Buffer:可視部分を制限するオブジェクト(マスクのように)。黒と白のイメージで構成される

Buffer Object は OpenGL がサーバーサイドと呼ぶ保管庫である*。他のバッファと異なり、データとして保持される期間が長い。3D オブジェクトの最適化された情報をもち、その種類は Structures(構造)と Indices(複:index)に分けられる。
(* 原文:Buffer Objects is a storage which OpenGL calls “server-side”)

Structures は頂点配列やテクスチャ座標配列などの3Dオブジェクトを描画するための情報のリスト。Indices は例えば3Dオブジェクトのある面がどうやって構成されているかを表す。

たとえば3次元空間の立方体について考える(元ネタサイトの画像をみるべし)。1つ1つの面は四角形だが OpenGL は扱えないので三角形にして、全部で12の三角形で描くことになる。

<以下意訳あり>
ここで、
  • 1つめの面は頂点Aと頂点Bと頂点Cをつないだ面
  • 2つめの面は頂点Bと頂点Cと頂点Dをつないだ面
  • 3つめの面は頂点Dと頂点Eと頂点Fをつないだ面
  • ・・・・・
とデータを持つこともできるが、効率が悪い。例えば1つめと2つめの面は線分BCを共有しており、それぞれの面が頂点の情報をそっくりそのまま持つのは無駄。

ではどうするかというと、頂点は頂点だけの情報リストとして持ち、面はその点情報のどれを使うかということだけを記録する。
vertex0 = new Vertex(20,20);
vertex1 = new Vertex(40,20);
vertex2 = new Vertex(40,40);
vertex3 = new Vertex(20,40);

vtxList = [vertex1, vertex2, vertex3, vertex4];

tri0 = new Triangle(0,1,2);
tri1 = new Triangle(1,2,3);
以上のコードで tri0 は vtxList 内の 0, 1, 2にある要素、つまりvertex0, vertex1, vertex2 で構成されることを表すことができる。ここでは三角形が2つだけなのでかえってデータ量は多くなっているが、これが膨大な量になったときは言わずもがな。

OpenGL の実際では以下のようになる。
vtxList = [vertex1, vertex2, vertex3, vertex4];
triList = [0,1,2,1,2,3]; 
この配列のデータが3で区切られて利用される。つまり1つめの面は vtxList 内の0, 1, 2の頂点で構成され、2つめの面はvtxList 内の1, 2, 3の頂点で構成される、となる。

ここでバッファの話に戻ると、vtxList が Structures、triList が Indeces となる。

・ラスタライズ

ラスタライズとは3次元のすべての情報(頂点・座標・数学など)を2次元へ変換する処理である。スクリーンは2次元だからね。

しかしこれをやるかやらないかは機器メーカーの勝手。OpenGL を管理している Khronos group は OpenGL のためにルール(EGL という API)を提供しているが、メーカーはこれを改変することができる。

よって OpenGL の API で直接に Frame Buffer に書きこむのではなく、各メーカーが決めているルールに従って描画処理を書く必要がある。このルール(= API) はメーカーから提供される。



OpenGL’s pipelines


ここで2つのパイプライン
  • Programmable pipeline:プログラム可能なパイプライン
  • fixed pipeline:固定パイプライン
がある。Programmable pipeline はまじで大変。

Programmable pipeline では開発者はカメラ・ライト・材質・効果などすべてのことを設定しなければならない。そしてこの設定はかの有名なシェーダーで行うことができる。Programmable pipeline はシェーダーだと捉えられることが多い。

Programmable pipeline はコードとしては断片的だが GPU の複雑な計算に直接につながっている。複雑ってどういうことかというと、あるピクセルの色を決めようとしたら、テクスチャの情報・ライトの情報(色、角度、位置、強さetc)・カメラの情報(色、角度、位置etc)、・・・といったように多数の情報が必要になる。でもってこれを設定しなければならん、と。

fixed pipeline はこれらのすべての情報をまとめて僕らに提供する。

シェーダーの設定にはC言語によく似た言語、OpenGL Shader Language (GLSL) を使う。Open GL ES は機能制限があり、OpenGL ES Shader Language (GLSL ES or ESSL) という言語になる。構文などは同じで使える範囲が異なるだけ。

シェーダーは頂点シェーダー(VS, or VSH) とフラグメントシェーダー(FSH) の2つではたらく。

頂点シェーダーはそれぞれの頂点で実行される小さなプログラム。頂点の最終的な位置を決める処理を行う。立方体ならその頂点の数だけ、つまり8回処理が行われる。

頂点自体の空間上の位置に加えて視点(カメラ)の情報も決定する必要がある。フラグメントシェーダーにいくつかの情報を渡さなければいけないが、僕らは頂点シェーダーを通さなければフラグメントシェーダーにアクセスすることができない。


立方体を眺めてみよう。立方体には8つの角と6つの面があるが、頑張っても同時に見ることが出来るのは7つの頂点とそれから構成された3つの面だけ。この「どのように見えるか」を処理するのがフラグメントシェーダーの仕事。テクスチャ・カメラ・ライト・重なりなどの要素をすべて考慮してスクリーン上のピクセルの色を決定する。

頂点シェーダーとフラグメントシェーダーが一緒に(1体1対応で)動いているということを知っておくこと。 結局のところ、 OpenGL のプログラムは頂点シェーダーとフラグメントシェーダーのコンパイルされた1対のものであって、それ以上のものではない。



Conclusion(まとめ)

  1. OpenGL のロジック部分は3つの Primitives から構成されている。
  2. fixed pipeline は重いしでかい。機能が固定されている。Programmable pipeline は簡単で速くて、いろいろ設定もできる。
  3. Programmable pipeline はシェーダーと同義語。頂点シェーダーとフラグメントシェーダーの2つで構成され、最終的にはプログラムの中にコンパイルされる。

と、このように理解するのは簡単だが、学ぶとなると、、、うーん。

次の2パートで実際のコーディングなどを見ていくが、もう1つの OpenGL のコンセプトを紹介しよう。


OpenGL’s Error API


港の例で紹介したように、OpenGL の内部に直接アクセスすることはできない。もしその内部でエラーが起こったらアプリケーションは止まってしまう。

この内部エラーを知るために、OpenGL から API が提供されている。2つの種類があって、1つはエラーがあるかを返すもの。もう1つはエラー内容を知るためのもの。

「チェック → エラー内容チェック」の流れを必要な箇所(バッファやシェーダの設定時など)できちんと行いましょう。


以上。


この著者さんは良い意味で OpenGL love なギークだなあという印象。でもって一般に面倒だと言われる Programmable pipelineを強烈プッシュ。

英語を流し読みする感覚がぼちぼち戻って来ましたが、次のパートは超膨大なのでどうしようかと考え中です。

"storage" の良い和訳ないですかねえ。保管庫にしてしまいましたが。

0 件のコメント:

コメントを投稿