ゲームなどのリアルタイムレンダリングでは、例えば60FPSの場合1フレームの描画にかけられる時間は最大でもわずか16ミリ秒程度であり、また頂点情報やテクスチャデータの格納・参照に使用できるビデオメモリ(グラフィックスカードに直結実装されたVRAM)の容量といった制約条件が多い[2]。そのため、リアルタイム用途のシェーダーでは、相互反射などを考慮しない、低品質だが簡潔で高速な局所照明(ローカルイルミネーション)ベースの陰影計算モデルやZバッファ技法が用いられることが大多数である。GPUの進化とリアルタイム用プログラマブルシェーダーの発展を受けて、アルゴリズムやデータ構造を工夫してグローバルイルミネーションをリアルタイム実装している例(PRT[3]、ライトフィールド[4]、ISPM[5]、SVO-GI法[6]やNVIDIA GI WorksのCLIPMAP法[7]など)も出てきているが、高性能なハードウェアを要求するなど、2018年時点でも未だ発展途上の技術である。シャドウや多光源環境のライティングに関しても、CSM[8]/PSSM[9]といった種々のシャドウマップ派生技術、および遅延シェーディング・遅延ライティングなどが考案されているが、時間および資源の制約が足かせとなり、品質や柔軟性はプロダクションレンダリングに及ばない。
リアルタイム用途のシェーダーはしばしばCGプロダクションソフトウェアのプレビューにも用いられる。最終出力に必要な高資源・高品質なレンダリング(レンダリング方程式(英語版)に基づくレイトレーシングやラジオシティ、フォトンマッピングなど)の代わりにリアルタイム用途のシェーダーを用いることで素早いプレビューが可能になる。例えばAutodesk 3ds Max、Autodesk Maya、Autodesk Softimage、およびNewTek LightWave 3Dはいずれもプログラマブルシェーダーによるプレビューを実装している。また2DCGソフトウェアにもアクセラレータとしてしばしば導入される(例: Adobe PhotoshopやAdobe Flash)。GUIベースオペレーティングシステム (OS) のデスクトップ合成エンジンや標準2DグラフィックスAPIでも、Windows Aero/Direct2D (Windows) やQuartz Extreme/Core Image (macOS) などのように、GPUおよびプログラマブルシェーダーが活用される。
多くのリアルタイム用途グラフィックスパイプラインは複数段のプログラマブルシェーダーと固定処理からなっている。プログラム可能な1つの段階(シェーダー)はシェーダーステージと呼ばれる[10]。以下は典型的なシェーダーステージである。
表: シェーダーステージ
ステージ名
|
入力
|
出力
|
注記
|
頂点シェーダー
|
頂点
|
頂点
|
|
テッセレーション[11]
|
プリミティブ
|
プリミティブ
|
しばしば "制御シェーダー" + "テッセレーション固定機能"
|
ジオメトリシェーダー[12]
|
プリミティブ
|
プリミティブ
|
|
(ラスタライズ)
|
プリミティブ
|
フラグメント
|
固定機能
|
フラグメントシェーダー
|
フラグメント
|
フラグメント
|
|
OpenGL 3.2以降とDirect3D 10[13]以降では3種類のシェーダーを使用できる。
頂点シェーダー(英: Vertex Shader, VS)は主に入力頂点を座標変換(トランスフォーム)するための機能である。頂点シェーダーはオブジェクトを構成する頂点の集合(頂点配列、頂点バッファ)に対してのみ作用し、例えば3次元空間におけるXYZ位置座標や法線ベクトル、色、テクスチャマッピング座標(UV座標など)といった頂点の属性だけを参照・変換できる。3次元グラフィックスにおける主な座標変換にはワールド変換、ビュー変換、プロジェクション変換、およびビューポート変換が存在するが、頂点シェーダーが担当するのはワールド・ビュー・プロジェクション変換である。ワールド・ビュー変換はモデル・ビュー変換とも呼ばれる。なお頂点単位のライティングであれば、頂点シェーダーで座標変換のほかにライティング計算を行なうこともできる。頂点シェーダーで計算された頂点情報はジオメトリシェーダーに渡されるか、そのままピクセルシェーダーに渡される。
Direct3D 9では頂点シェーダーをソフトウェアエミュレーションすなわちCPUで実行することもできるが(D3DCREATE_SOFTWARE_VERTEXPROCESSING
)、プログラマブルシェーダー機能に対応しているハードウェア(GPU)であれば多数のプロセッサコアを使用して並列に処理を行なうことができるので、CPUで頂点シェーダーを実行するよりもGPUを利用した方が高速になる。
なお、シェーダーモデル3.0(DirectX 9.0c、OpenGL 2.x世代)で導入されたVertex Texture Fetch (VTF) [14]を使用すると、頂点シェーダーステージでテクスチャデータを参照することも可能となる。
シェーダーモデル4.0(DirectX 10世代)以降では、VTFはバッファデータの参照とともに標準化されている[15](OpenGLは3.1でVTFを標準化し、バーテックスシェーダーで少なくとも16個のTIUを使えるようになっている)。
さらに、OpenGL 4.2ではすべてのシェーダーステージにおいてイメージオブジェクトに対するロード/ストアを可能にする機能が標準化された[16]。DirectXにおいても、バージョン11.1にて、ピクセルシェーダーやコンピュートシェーダーだけでなく、すべてのシェーダーステージにおいて各種リソースに対する書き込みが可能になっている[17]。
ジオメトリシェーダー(英: Geometry Shader, GS)はピクセルシェーダーに渡されるオブジェクト内の頂点の集合を加工するために使用される。ジオメトリシェーダーにより、実行時に頂点数を増減させたり、プリミティブの種類を変更したりすることが可能となる。OpenGLではプリミティブシェーダーとも呼ばれる。
ジオメトリシェーダーはポイント、ライン、トライアングルといった既存のプリミティブから新しいプリミティブを生成できる。
ジオメトリシェーダーは頂点シェーダーの後に実行され、プリミティブ全体または隣接したプリミティブの情報を持つプリミティブを入力する。例えばトライアングルを処理するとき、3つの頂点がジオメトリシェーダーの入力となる。ジオメトリシェーダーはラスタライズされるプリミティブを出力でき、そのフラグメントは最終的にピクセルシェーダーに渡される。またプリミティブを出力せずにキャンセルすることもできる。
ジオメトリシェーダーのよくある使い方としては、ポイントスプライトの生成、ジオメトリテセレーション、シャドウボリュームの切り出し、キューブマップあるいはテクスチャ配列へのシングルパスレンダリングなどがある。
ピクセルシェーダー(英: Pixel Shader, PS)はピクセル単位のライティングやポストプロセス(後処理)を行なうための機能である。ピクセルシェーダーはラスタライズされるプリミティブの各ピクセルに影響する。また、ピクセルシェーダーにてテクスチャを参照することでバンプマッピングやフォグ、シャドウ、ブルームといったエフェクトを最終レンダリング結果に適用することもできる。OpenGLではフラグメントシェーダー(英: Fragment Shader, FS)と呼ばれる(Fragment: 断片)。
ピクセルシェーダーはピクセルを操作する機能であり、頂点シェーダーもしくはジオメトリシェーダーから入力された情報を元にテクスチャを合成したり表面色を適用したりする。ピクセルを操作する処理にかかる時間は入力プリミティブのラスタライズ後のピクセル数や出力解像度に左右されるため、通例は頂点単位の処理と比較して高負荷である。これをピクセルシェーダープログラムとしてプログラミングし、高い並列処理性能を持つGPUで実行することにより、バンプマッピング等のより高度なエフェクトをCPUですべて実行するよりもはるかに高いパフォーマンスで実現できる。なお、通常のレンダーターゲットを使ったピクセルシェーダーでは、アルファブレンド(アルファ合成)処理の詳細をプログラムすることはできない。
Direct3D 9世代までのGPUでは、頂点シェーダーおよびピクセルシェーダーを担当するハードウェアユニットの数はそれぞれ製品ごとに固定されていたが、Direct3D 10世代の統合型シェーダーアーキテクチャ(Unified Shader Architecture)では各シェーダーユニットが統合され、複数の汎用シェーダーユニットを使って上記3つのシェーダーステージに動的に振り分ける形となる[18] [19] [20] [21]。
これらのシェーダーはGPUのパイプライン内で実行される。下記はパイプライン内にどのように埋め込まれているのかを示す例である。
- CPUは命令とジオメトリデータをGPU側に送信する。
- 頂点シェーダー内でジオメトリを変換し、頂点単位ライティングの計算などを実行する。
- ジオメトリシェーダーを使用する場合は、頂点シェーダーが出力したジオメトリに対してプリミティブ増減や変更を行なう。
- これまでの処理で計算されたジオメトリをトライアングルセットアップに設定する。トライアングルはquadに変換される(1つのquadは2 × 2ピクセルのプリミティブである)。
- ピクセルシェーダーを適用し、ピクセル単位ライティングなどを実行する。
- 視界判定を実行する。もし視界内にある場合はフレームバッファにピクセルを書き込む。
頂点シェーダー/ジオメトリシェーダーの出力をラスタライザーおよびピクセルシェーダーに渡すのではなく、バッファやテクスチャなどのメモリリソースに書き出した後、インプットアセンブラーや頂点シェーダー/ジオメトリシェーダーに再入力する、ストリームアウトプットという機能も存在する[22](OpenGLにはトランスフォームフィードバックと呼ばれる類似機能が存在する)。ストリームアウトプットはこれまでのようにGPU側でデータを参照するだけでなく、GPU自身が書き換えたデータを(CPUを介することなく)再利用することができるため、GPUパーティクルなどに応用できる[23]。
OpenGL 4.0以降とDirect3D 11以降ではさらにテッセレーションシェーダーをオプションとして使用できる。
RenderMan Shading Languageに代表されるように、プロダクション向けの3次元コンピュータグラフィックスのレンダリングでは古くからシェーディング処理をプログラムで記述してカスタマイズし、高品質な映像を作り出すといったことは当たり前のように行なわれてきた。一方でCADソフトやゲームなどのリアルタイム 2D/3Dグラフィックス アプリケーション開発者は、ソフトウェア(CPU)による定形の簡易シェーディングあるいはグラフィックスハードウェア(グラフィックスカードにおけるGPU)に固定機能として実装されていた頂点変換・シェーディング機能(ハードウェアT&L)すなわち「定形のパイプライン」(固定機能パイプライン)を使用してグラフィック表示を行なっていた[24]。
しかし、多数のグラフィック表現技術が次々と開発されていく中で、それらの技術をGPUメーカーが逐一ハードウェアに機能として実装していく形態は非効率であり、またユーザープログラマーが新しい技術を試すにはメーカーの対応を待たねばならなかった。この問題を解決するアイディアとして、GPUのパイプラインをソフトウェアプログラムにより組み立てる「プログラマブル パイプライン」と呼ばれるアーキテクチャが構築されることになる。以前は完全にブラックボックスだったグラフィックスパイプラインがユーザープログラマーに対して開放されることで、新たな陰影処理技法や各種エフェクト(画面効果)を試験的に実装してGPU上で走らせることが容易になり、先進的なGPUの機能を利用する優れた柔軟性と、表現力の爆発的な向上がもたらされた。
当時のOpenGL・DirectX (Direct3D) のAPIによってブラックボックスとして提供されていたシェーダーは固定機能シェーダーと呼ばれ、プログラマブルシェーダーと区別される。OpenGLにおいてはバージョン1.5からプログラマブルシェーダーが拡張として導入され、バージョン2.0からは標準化された。Direct3Dにおいてはバージョン8からプログラマブルシェーダーが導入された[25]。OpenGL 3.1、OpenGL ES 2.0、およびDirect3D 10以降は固定機能シェーダーが廃止され、グラフィックスパイプラインはプログラマブルシェーダーによって記述される。現在ではリアルタイム処理系において「シェーダー」といえばプログラマブルシェーダーを指す[26] [27]。また、Direct3D 9世代までは、頂点シェーダーとピクセルシェーダーの処理を担当するGPU内のハードウェア演算ユニット(演算器)は完全に分かれており、それぞれの演算ユニットのことを「シェーダー」と呼んでいた。そのため、かつてはグラフィックスカードのハードウェア性能指標として演算ユニット数を表すために「シェーダー数」という言葉が使われていたが、統合型シェーダーアーキテクチャを採用したDirect3D 10世代以降は「ストリームプロセッサ数(SP数)」あるいは「CUDAコア数」という言葉が用いられるようになっている[28] [29] [30] [31] [32]。