夏休みゼミするぞ
なんだかんだで夏休みも半分過ぎつつありますが、この夏休みに私が研究室の皆さんに対して何もしないってのもなんだか具合が悪い気がします。そこで、とりあえず OpenGL に関するゼミをしようと思います。OpenGL に関しては、もう長いこと「手抜き OpenGL」をベースにやってきましたけど、やはり時代はどんどん変わりますし、いつまでもこの内容でやってるというのも芸がないと思いますので、少しは今風の内容を盛り込んでみたいと思います。私も勉強しながらやっていますので、よろしくお付き合いください。
OpenGL とは
OpenGL は、プログラムからグラフィックスハードウェアを制御するために使用する、アプリケーションプログラムインタフェース (API) のひとつです。これはグラフィックスハードウェアがもつ機能をそのまま表現したものですから、プログラミング言語ではアセンブラに相当する、低位の (ハードウェアに近い) グラフィックスライブラリです。したがって、これだけを使って CG アプリケーションを作成することは、少々面倒な作業かもしれません。
もし、グラフィックスハードウェアの機能を直接制御しなければならない理由がなければ、より高位の (必要な機能が整備された) グラフィックスライブラリやアプリケーションソフトウェアを使用したほうが、目的を達成するのにかかる手間を省くことができます。例えば、3次元空間のシーンをソフトウェアで表現するのであれば、Open Inventor や Open Scene Graph, SceniX のようなシーングラフ API が便利かもしれません。またゲームの開発には、Irrlicht や OGRE, Crystal Space, それに Unity といったゲームエンジンが使えます。単に3次元 CG のシーンを作りたいというのであれば、Blender や Maya, 3ds Max, Softimage (3つとも Autodesk ってのは…) あるいは LightWave 3D といった CG ソフトウェアを使うべきでしょう。
しかし、そのような場合でも、OpenGL などの API の知識は有用です。Blender や Maya, LightWave 3D のような CG ソフトウェアですら、OpenGL に含まれる GLSL (OpenGL Shading Language) により記述したシェーダを使うことができます。また OpenGL は、実は CG の基本的な理論に忠実に実装された API でもあるので、CG の理論の実装例として OpenGL を学ぶことも、意義のあることだと考えています。
レンダリングパイプライン
形状データから画像を生成する方法は、サンプリングによる方法 (レイキャスティング法、レイトレーシング法) とラスタライズによる方法 (デプスバッファ法、スキャンライン法など) の二つに大別されます。後者は座標変換により形状データのスクリーンへの投影像を求め、それを走査変換によりスクリーン上で画素に展開 (ラスタライズ) して画像を生成します。これはおおよそ以下の手順に整理されており、レンダリングパイプラインと呼ばれています。

最初、この手順はソフトウェアで実装されていました。しかし、CG というのは何らかの計算結果を画像化することが目的なので、画像化の作業にも CPU を使うことは無駄だと考えられました。そこで最初に、単純な整数計算とメモリアクセスの繰り返しである走査変換が、専用のハードウェアで実現されました。これにより画像生成の速度が劇的に向上したとともに、CPU は画像化の作業から開放され計算に専念できるようになりました。

モデル変換やビュー変換 (この二つはまとめてモデルビュー変換とも呼ばれます), および投影変換といった座標変換や、陰影計算 (shading) には、実数計算が含まれます。しかし、高速な実数計算ハードウェアはコストが高かったので、当初グラフィックスハードウェアには搭載されていませんでした (32bit CPU でも初期のものには内蔵されていませんでした). この部分をハードウェアで実装したこと (ハードウェア T & L - Transform and Lighting) によって、リアルタイム 3D CG が本格的に利用されるようになってきました。

レンダリングパイプラインの処理内容はここまであまり変化してこなかったので、ハードウェア化はレンダリングパイプラインの各段階の機能をハードウェアで実現する形で行われました (固定機能ハードウェア). このため新しい機能が必要になるたびに、それを実現するハードウェアが追加されてきました。しかし CG の応用が広がり多様な表現が要求されるようになってくると、こうして機能を追加することが難しくなってきました。そこで、機能を追加する可能性がある部分をプログラム可能にして、ソフトウェア的に機能を拡張できるようにすることが考えられました。
それまで座標計算と (頂点の) 陰影計算を担当していたハードウェアはバーテックスシェーダに置き換えられ、走査変換により選ばれた画像の各画素の色を頂点の陰影の補間値やサンプリングしたテクスチャの値を合成 (マージ) して決定していたハードウェアはフラグメントシェーダに置き換えられました。また、フラグメントシェーダから光源情報や材質情報を参照することや、テクスチャをバーテックスシェーダから参照すること (vertex texture fetch) も可能になりました。

さらに、それまでアプリケーションソフトウェア側で行われていた基本形状 (点、線分、三角形) の生成の一部を、グラフィックスハードウェア側で行うことができるようにもなりました。これにより、アプリケーションソフトウェア (CPU) からグラフィックスハードウェアに送られる形状データの量を減らしながら、より高品質な形状の表現が行えるようになりました。これはジオメトリシェーダにより行われます。なお、ジオメトリシェーダの利用はオプションなので、使用しなくてもかまいません。

ジオメトリシェーダを使用する場合は、グラフィックスハードウェアに入力できる基本形状が拡張され、曲線や曲面を表現することが容易になっています。また、Point Sprite もこの機能を使って実現することができます。

なお今回のゼミでは、まだジオメトリシェーダを利用できる環境 (OpenGL 3.2) を整備できていないため、ジオメトリシェーダに関する説明は行いません (まだ知らないし).
今風の OpenGL の使い方
このようにグラフィックスハードウェアは、レンダリングパイプラインをハードウェアで実現したものであり、OpenGL はそれを反映しています。したがって、グラフィックスハードウェアのアーキテクチャが変化すれば、それに対応して OpenGL も変化しなければなりません。グラフィックスハードウェアがプログラム可能になったことにより、それまで固定機能ハードウェアにより実現されていたグラフィックスパイプラインの機能の多くが捨て去られました。
もちろん「互換プロファイル」を使用すれば、従来の機能をそのまま利用できます。しかし本来の OpenGL は、それ自身がグラフィックスの処理方法を提供するものから、ユーザがグラフィックスの機能を実装することを支援するものに立場を変えています。したがってユーザは、これまで OpenGL が提供してきた枠組みにとらわれることなく、自由にグラフィックスの処理方法を実装することができます。
もうひとつ、最近のグラフィックスハードウェアの特徴として、このプログラム可能性のほかに、大量のメモリを搭載していることが挙げられます。これまではアプリケーションソフトウェアからグラフィックスハードウェアに基本図形を送り続けて画像を生成していました。
これに対して最近では、グラフィックスハードウェア側のメモリに基本図形を保持しておき、それを繰り返し使って画像を生成するようになってきています。これにより、遅い外部バスを介した基本図形の転送を最小限に抑えることができ、グラフィックスハードウェアの性能を最大限に発揮できるようになります。今回のゼミでは、この二つを焦点に、「今風の OpenGL の使い方」について説明を試みたいと思います。