けんごのお屋敷

2014-12-27

OpenGL が世界を描画する仕組み

OpenGL 基礎シリーズの第 1 回です。

ビューイングパイプライン

OpenGL が世界を描画するっていうタイトルだけでワクワクするなら、あなたはもう OpenGL の、いや、このブログの虜です。というのはおいといて、OpenGL で描画すると一言で言ってもその描画フローの中には大量の計算処理が含まれおり、そのフローをひと通り全て通り超えてこそ初めて画面に何か描画される。いわゆるパイプラインと呼ばれる処理の流れがあり、それはおおまかには以下のようになっている。細かくは他にもやってることはあるし厳密には実際と異なる部分もあるけど、これくらい抑えておけば基本はわかると思う。

ちなみに赤い部分は自分でプログラミングする必要がある。青い部分は設定値さえこちらから与えてやれば後は OpenGL が内部でよろしくやってくれる。

ビューイングパイプライン

ふー、ただ単に 1 枚の画像を描画するだけでもこれだけのフローがあって、そして突然わけもわからない単語が大量に出てくるもんだからブラウザを「そっと閉じる」人がいるかもしれないけど、まあ正しい反応かもしれない。そういう人達は素直に Cocos2d-x とか Unity とかでゲームを作るほうが幸せを感じられると思う。Cocos2d-x や Unity などの便利ツールは実は中でこんなことをやってるのだ。恩恵は計り知れない。ありがとう Cocos2d-x!ありがとう Unity!

さて、フローの各部分の詳細は別途記事にするとして大事な部分だけの簡単な概要だけをまずはどうぞ。

アプリ側でデータの準備

OpenGL では、どこに何を描画するかは頂点座標によって決まるので、頂点座標は必須で準備する必要がある。そして、描画する物体に対して移動・回転・拡大・縮小などの操作を加える場合には変換行列も必要になる。さらに、画像を表示したい場合はテクスチャの ID と UV 座標も必要になる。私達開発者は、こういった各種データをアプリケーション側で準備して、OpenGL が提供している API を介してデータを GPU に転送する。

いやしかし、頂点座標とか変換行列とか UV 座標とかわけのわからない単語がまた出てきて、突然のウルトラヴァイオレット!イミフ!!!ですが、でも大丈夫!ちゃんと別の記事で詳しく触れていくので。ちなみに UV はウルトラヴァイオレットじゃないよ。

シェーダ

そしてまたすぐにわけのわからないシェーダとかいう単語が出てくる。シェーダって何だろう?Wikipedia によると

シェーダ(英: shader)とは、3次元コンピュータグラフィックスにおいて、シェーディング(陰影処理)を行うコンピュータプログラムのこと。「shade」とは「次第に変化させる」「陰影・グラデーションを付ける」という意味で、「shader」は頂点色やピクセル色などを次々に変化させるもの(より具体的に、狭義の意味で言えば関数)を意味する。

「はいはい、イミフイミフ」

いつも思うけど Wikipedia ってわざと難しく書いてない?さて、まず 2D だったら x 座標と y 座標を指定して描画するメソッドを呼び出せば簡単に画像でも線でも表示できた。できたんだけど、じゃぁ 3D はどうかっていうとそう簡単にはいかなくって、別途 3D 空間を描画するためのプログラムが必要になってくる。その描画するプログラムのことをシェーダと言って、シェーダにはいくつか種類があるんだけどとにかく OpenGL の場合、このシェーダプログラムがないといくら描画メソッドを呼び出しても画面には何も表示されない。

さて、そしてここから重要。OpenGL ES 1.x の頃は OpenGL に組み込みのシェーダが存在しており固定機能パイプラインとかって呼ばれてたけど、それに対して OpenGL ES 2.0 以降では組み込みのシェーダがなくなってしまった。つまり 自分でシェーダを作らないといけなくなった。聞いたことあるかもしれないけど、こういう自分で作るシェーダのことを プログラマブル(プログラム可能な)シェーダ と呼ぶ。ちなみに OpenGL のシェーダプログラムは GLSL (OpenGL Shading Language) という言語を使って開発する。

OpenGL Shading Language

新しい言語まで勉強しないといけないの!もうダメだ、私のライフはゼロよ!

いえ、(この記事では) そんなことはありません。ここでは最低限 2D 描画の解説が目標なので 2D 描画に必要最低限のコード程度しか取り扱わない。むしろ GLSL という言語自体よりも、シェーダが何をするのか、どういう仕組みなのか、を主とするので GLSL をバリバリ使って 3D を華麗に扱う技術の紹介は他サイトに譲る。

では OpenGL で扱うことのできるシェーダとはどんなものだろうか?バーテックスシェーダとフラグメントシェーダの概要をみてみよう。

バーテックスシェーダ

最初に書いた通り OpenGL では 3D 空間の頂点情報 (x, y, z) を GPU に渡して処理をするけれど、実際に描画するディスプレイは 2D なので、渡した頂点情報が最終的にディスプレイのどこに位置することになるのかを計算してあげる必要がある。その計算をしてくれるのがバーテックスシェーダで、GPU に渡した頂点の数だけバーテックスシェーダが実行されることになる。

それぞれの座標変換の過程は別の記事で詳しく触れていくので、心配せずに。

バーテックスシェーダの座標変換

フラグメントシェーダ

バーテックスシェーダは頂点情報の計算だったけど、フラグメントシェーダは色情報の計算をする。フラグメントシェーダは、バーテックスシェーダによって計算された最終的な頂点座標で構成される図形のピクセル数だけ実行され、各ピクセルにどういう色を付けるのかを求める。各ピクセル毎ということでお分かりのように毎フレーム大量に実行される。

たとえば画像を表示する場合、画像のディスプレイ上での表示位置はバーテックスシェーダが決めることになるけど、画像の色情報を元にディスプレイのどのピクセルにどの色を描画したらいいのかをフラグメントシェーダが計算して、そのピクセル位置のデプス値やアルファ値を元に最終的に出力される色が決定する。

フラグメントシェーダでの色の計算

ラスタライズ

先にシェーダをまとめて説明してしまった都合で順番が逆になってしまったけど、頂点情報がバーテックスシェーダによって変換された後 (正確にはその後にビューポート変換という処理がはさまる)、次にラスタライザというプログラムに処理が渡ってくる。ラスタライザは渡された頂点情報を元に、ディスプレイ上のどのピクセルに描画すればよいのかを計算して画素を生成する。この処理のことをラスタライズと言って、ラスタライズによって生成された画素のことをフラグメントと言う。フラグメントシェーダはラスタライズによって生成されたフラグメントの情報を入力として、各フラグメントの色の出力を決定していく。(図はわかりやすいように画素のサイズを目に見える大きさにしてるけど、実際のディスプレイの 1 画素はもっと細かいのでこんなギザギザにはならずもっと綺麗になる)

ラスタライズ処理についても別の記事で詳しく触れていく。

ラスタライズの概要

まとめ

OpenGL の描画までのフローをおおまかに見てみた。各セクションの詳しい話はこの後の記事で 1 ずつ触れていく。

次回 頂点情報とプリミティブ に続く。

  • このエントリーをはてなブックマークに追加