はじめに
OpenGLを全く使ったことがない人を対象に、全体的なイメージを掴んで、基本的な描画ができるようになることを目標にします。
WebGLもOpenGL ES 2.0をベースにしていますので、同じように理解できるはずです。文中でOpenGLと書いた場合、OpenGL ES 2.0を指すと思ってください1
まずはコードを動かすことよりも、感覚的にイメージを掴んでもらうことを目指します。全体像が掴めないと、コードを見ても何をやっているのか?どういう仕組みで動いているのか?理解しづらいためです。
GPUの概要
CPUの並列処理とGPUの並列処理は大きく異なっています。マルチコアCPUでは、各CPUは別のプログラムを並列実行できます。一方GPUの各演算ユニットは__全く同じプログラムを異なるデータに対して並列実行__します。
これはGPUの仕組みを簡略化して図示したものです。入力データは各演算ユニットにバラバラに渡されます。プログラムと共通設定値(GPU機能の設定やプログラムで使われる共通値)は全ての演算ユニットで同じものが利用されます。各演算ユニットが並列で動作し、出力値を得ます。
GPUの演算ユニットは単純な処理を高速実行するように設計されていて、条件分岐が非常に苦手です。実行できるプログラムにはCPUと違ってかなりの制約があります。
実際のGPUの仕組みはもっと複雑ですが、とりあえずOpenGL入門としてはこの程度の理解で十分です。
OpenGL ES 2.0 の概要
処理全体の流れ
OpenGLの処理全体の流れを図示しました。
セットアップが完了したら、画面更新の度にレンダリング処理を行います。レンダリング処理の先頭で準備を行い、各図形の描画を行い、最後に結果を画面に表示します。
各図形のレンダリングの流れ
各図形のレンダリングの流れをものすごく簡略化して図示してみました。
入力値として頂点データを渡します。頂点データは各頂点ごとの情報の集まりで、各頂点の情報には座標はもちろんそれ以外の情報(色やテクスチャ位置など)も含みます。
頂点データは__頂点シェーダ(Vertex Shader)__というプログラムに渡され、各頂点ごとに別々の演算ユニットで並列実行されます。ここで画面上のどの位置に各頂点が描画されるのか、図形の画面上での形状が決定します。また次の断片シェーダに渡す値の計算も行います。
ラスタライズ処理で図形が実際の画面のピクセルに対応付けられます。
次に__断片シェーダ(Fragment Shader)__というプログラムが各ピクセルごとに別々の演算ユニットで並列実行され、各ピクセルの色が決定されます。
これを繰り返して図形を描画していきます。単純に実行順で重ね合わせることもできますが、各ピクセルごとの深さ値(depth)で何が手前にくるか決定させることもできます。3D描画では視点からの距離を深さ値にすることで、手前にあるもので奥にあるものが隠れるように描画させられるわけです。
頂点データに何を含めるかや、頂点シェーダから断片シェーダに何を渡すかは、プログラムで何を行うかで決めることになります2。
もう少し具体的に各処理で何が行われるのかを図にしてみました。三角形を一個描画しています。
頂点データに含まれる頂点座標は、プログラマが自由に決めた座標空間内の座標で構いません。これを頂点シェーダに渡して、OpenGL座標空間内の座標に変換します。そして各ピクセルごとに断片シェーダが実行されて色が決まります。
OpenGL座標空間は次のように考えてください。x座標の-1.0が画面左端、1.0が画面右端、y座標の1.0が画面上部、-1.0が画面下部、z座標が深さ値に対応します。画面にはz座標で手前にある物が上に描かれるように重ね合わされます(深さテストをONにしている場合)。
頂点シェーダから断片シェーダへ渡す値
まずはこの描画結果を見てください。三角形の3つの頂点に座標と色を与えて描画したものです。色はそれぞれ青、赤、緑を指定しています。
見事にグラデーションしていますね。以下は今回の処理を図にしたものです。
頂点データのデータ構造は行いたい処理に合わせてプログラマが自由に設計します。ここでは座標と色を渡しています。頂点シェーダから断片シェーダへ渡す値も自由に決められます2。ここでは色をそのまま渡しています。断片シェーダでは渡された色をそのままピクセルの色として採用します。
今回の描画では頂点は3つですがピクセルはもっと沢山あります。頂点と同じ位置のピクセルの色は渡した色そのものでしょうけど、他のピクセルはどうなるのでしょうか?グラデーションしているのを見ればわかると思いますが、頂点シェーダから断片シェーダに渡される値は線形補完されます。
三角形は頂点が3つしかありませんので頂点シェーダでは3つの演算が行われるだけです。一方断片シェーダはピクセルの数だけ実行されます3。断片シェーダで重い処理を行うとパフォーマンスが劣化しやすいので注意してください。