
コンピュータグラフィックスにおいて、シェーダーとは、レンダリングパイプラインを通過するデータに適用されるプログラム可能な操作です。[ 1 ] [ 2 ]シェーダーは、頂点やプリミティブなどのデータに作用してジオメトリを生成または変形したり、フラグメントに作用してレンダリングされた画像の値を計算したりします。[ 2 ]
シェーダーは多種多様な演算を実行でき、様々なハードウェア上で実行できます。現代のリアルタイムコンピュータグラフィックスでは、シェーダーはグラフィックス処理装置(GPU)上で実行されます。GPUは、プログラムの高度な並列実行を可能にする専用ハードウェアです。画像のレンダリングは驚くほど並列であるため、フラグメントシェーダーとピクセルシェーダーはSIMDハードウェア上で高いスケーラビリティを発揮します。歴史的に、レンダリングの高速化を求める動きは、高度に並列化されたプロセッサを生み出し、それらを他のSIMD対応アルゴリズムに利用してきました。[ 3 ]計算パイプラインで実行されるこのようなシェーダーは、一般的にコンピュートシェーダーと呼ばれます。
「シェーダー」という用語が初めて一般に公開されたのは、ピクサーが1988年5月に最初に公開したRenderManインターフェース仕様のバージョン3.0でした。 [ 4 ]
グラフィックス処理ユニット(GPU)の進化に伴い、OpenGLやDirect3Dなどの主要なグラフィックスソフトウェアライブラリがシェーダーのサポートを開始しました。最初のシェーダー対応GPUはピクセルシェーディングのみをサポートしていましたが、開発者がシェーダーの威力を認識すると、すぐに頂点シェーダーが導入されました。プログラマブルピクセルシェーダーを搭載した最初のビデオカードは、2001年にリリースされたNvidia GeForce 3(NV20)でした。[ 5 ]ジオメトリシェーダーはDirect3D 10とOpenGL 3.2で導入されました。最終的に、グラフィックスハードウェアは統一されたシェーダーモデルへと進化しました。
シェーダーの従来の用途は、グラフィックスパイプライン内のデータを操作して画像のレンダリングを制御することです。グラフィックスシェーダーは、パイプライン内の位置、操作対象のデータ、および使用されるグラフィックスAPIに応じて分類できます
フラグメントシェーダー(ピクセルシェーダーとも呼ばれる)は、各「フラグメント」の色やその他の属性を計算します。フラグメントとは、最大で1つの出力ピクセルに影響を与えるレンダリング処理の単位です。最も単純な種類のピクセルシェーダーは、1つの画面ピクセルを色値として出力します。複数の入出力を持つより複雑なシェーダーも可能です。[ 6 ]ピクセルシェーダーは、常に同じ色を出力するものから、ライティング値を適用するもの、バンプマッピング、シャドウ、スペキュラハイライト、半透明などの処理まで、多岐にわたります。また、フラグメントの深度(Zバッファリング用)を変更したり、複数のレンダーターゲットがアクティブな場合に複数の色を出力したりすることもできます。3Dグラフィックスでは、ピクセルシェーダーだけでは複雑な効果を生成できません。これは、ピクセルシェーダーが単一のフラグメントのみを処理し、シーンのジオメトリ(頂点データなど)を認識できないためです。しかし、ピクセルシェーダーは描画対象の画面座標を認識しており、画面全体のコンテンツがテクスチャとしてシェーダーに渡された場合、画面とその周辺のピクセルをサンプリングできます。この技術により、ブラーや、カートゥーン/セルシェーダーのエッジ検出/強調など、幅広い2次元ポストプロセス効果を実現できます。ピクセルシェーダーは、パイプライン内のスプライトやテクスチャなどの2次元画像に中間段階で適用することもできますが、頂点シェーダーは常に3Dシーンを必要とします。例えば、ピクセルシェーダーは、ラスタライズされたビデオストリームのポストプロセッサまたはフィルタとして機能できる唯一のシェーダーです。
頂点シェーダーは、グラフィックプロセッサに与えられた3D頂点ごとに1回実行されます。その目的は、仮想空間における各頂点の3D位置を、画面に表示される2D座標(およびZバッファの深度値)に変換することです。[ 7 ]頂点シェーダーは、位置、色、テクスチャ座標などのプロパティを操作できますが、新しい頂点を作成することはできません。頂点シェーダーの出力は、パイプラインの次の段階、つまりジオメトリシェーダー(存在する場合)またはラスタライザーに送られます。頂点シェーダーは、 3Dモデルを含むあらゆるシーンにおいて、 位置、動き、照明、色の詳細を強力に制御できます
ジオメトリシェーダーはDirect3D 10とOpenGL 3.2で導入されました。以前はOpenGL 2.0以降でも拡張機能を使用することで利用可能でした。[ 8 ]このタイプのシェーダーは、グラフィックスパイプラインの先頭に送信されたプリミティブから、点、線、三角形などの新しいグラフィックスプリミティブを生成することができます。[ 9 ]
ジオメトリシェーダープログラムは、頂点シェーダーの後に実行されます。入力としてプリミティブ全体を受け取り、隣接情報も持つ場合があります。例えば、三角形を操作する場合、3つの頂点がジオメトリシェーダーの入力となります。シェーダーは0個以上のプリミティブを出力し、それらはラスタライズされ、そのフラグメントは最終的にピクセルシェーダーに渡されます。
ジオメトリシェーダーの典型的な用途としては、ポイントスプライトの生成、ジオメトリテッセレーション、シャドウボリュームの押し出し、キューブマップへのシングルパスレンダリングなどが挙げられます。ジオメトリシェーダーの利点を示す実世界の典型的な例としては、メッシュの複雑度の自動調整が挙げられます。曲線の制御点を表す一連のラインストリップがジオメトリシェーダーに渡され、必要な複雑度に応じてシェーダーは自動的に追加のラインを生成します。これらのラインはそれぞれ、曲線をより正確に近似します。
OpenGL 4.0およびDirect3D 11では、テッセレーションシェーダーと呼ばれる新しいシェーダークラスが追加されました。これは従来のモデルに、テッセレーション制御シェーダー(ハルシェーダーとも呼ばれます)とテッセレーション評価シェーダー(ドメインシェーダーとも呼ばれます)という2つの新しいシェーダーステージを追加します。これらを組み合わせることで、実行時に数学関数に従って、より単純なメッシュをより細かいメッシュに細分化できます。この関数はさまざまな変数、特にビューカメラからの距離に関連付けることができ、アクティブなレベルオブディテールスケーリングを可能にします。これにより、カメラに近いオブジェクトは細かいディテールを持ち、遠くにあるオブジェクトは粗いメッシュを持ちながらも、品質は同等に見えるようになります。また、非常に複雑なメッシュをメモリからダウンサンプリングするのではなく、シェーダーユニット内でメッシュを一度リファインできるため、必要なメッシュ帯域幅を大幅に削減できます。一部のアルゴリズムは任意のメッシュをアップサンプリングできますが、他のアルゴリズムではメッシュ内の「ヒンティング」によって最も特徴的な頂点とエッジを指示できます
2017年頃、AMD Vegaマイクロアーキテクチャは、ジオメトリを処理するために必要なデータにアクセスできるコンピュートシェーダーに似た新しいシェーダーステージ(プリミティブシェーダー)のサポートを追加しました。[ 10 ] [ 11 ]
Nvidiaは2018年にTuringマイクロアーキテクチャでメッシュシェーダとタスクシェーダを導入しましたが、これらもコンピュートシェーダをモデルにしています。 [ 12 ] [ 13 ] Nvidia Turingは、Ampere RTX 30シリーズがリリースされる数ヶ月前に、DirectX 12 Ultimate APIを介してメッシュシェーディングをサポートする世界初のGPUマイクロアーキテクチャです。[ 14 ]
2020年に、AMDとNvidiaは、DirectX 12 Ultimateを介してメッシュシェーディングをサポートするRDNA 2とAmpereマイクロアーキテクチャをリリースしました。[ 15 ]これらのメッシュシェーダーにより、GPUはより複雑なアルゴリズムを処理できるようになり、CPUからGPUに多くの作業をオフロードし、アルゴリズムを集中的に使用するレンダリングでは、フレームレートまたはシーン内の三角形の数を桁違いに増加させます。[ 16 ] Intelは、2022年第1四半期に出荷されるIntel Arc Alchemist GPUがメッシュシェーダーをサポートすると発表しました。[ 17 ]
レイトレーシングシェーダーは、 MicrosoftではDirectX Raytracing、Khronos GroupではVulkan、GLSL、SPIR-V、[ 18 ] 、 AppleではMetalでサポートされています。NVIDIAとAMDは「レイトレーシングシェーダー」を「レイトレーシングコア」と呼んでいます。統合シェーダーとは異なり、1つのレイトレーシングシェーダーに複数のALUを含めることができます。[ 19 ]
コンピュートシェーダーはグラフィックスアプリケーションに限定されず、 GPGPUと同じ実行リソースを使用します。グラフィックスパイプライン、例えばアニメーションやライティングアルゴリズムの追加ステージ(タイルフォワードレンダリングなど)などで使用される場合があります。一部のレンダリングAPIでは、コンピュートシェーダーがグラフィックスパイプラインとデータリソースを簡単に共有できます
テンソルシェーダはNPUまたはGPUに統合できます。テンソルシェーダは、MicrosoftではDirectML、Khronos GroupではOpenVX、AppleではCore ML、GoogleではTensorFlow、Linux FoundationではONNXによってサポートされています。[ 20 ] NVIDIAとAMDは「テンソルシェーダ」を「テンソルコア」と呼んでいます。統合シェーダとは異なり、1つのテンソルシェーダに複数のALUを含めることができます。[ 21 ]
計算カーネルは、高スループットアクセラレータ(グラフィックスプロセッシングユニット(GPU)、デジタル信号プロセッサ(DSP)、フィールドプログラマブルゲートアレイ(FPGA)など)用にコンパイルされたルーチンであり、メインプログラム(通常は中央処理装置上で実行)とは別個に使用されます。「 OpenCL C 」などの別のプログラミング言語でシェーディング言語で記述された「計算シェーダー」として指定することも、高級言語で記述されたアプリケーションコードに直接埋め込むこともできます。計算カーネルは計算シェーダーと呼ばれることもあり、 GPU上の頂点シェーダーやピクセルシェーダーと実行ユニットを共有しますが、1つのクラスのデバイスやグラフィックスAPIでの実行に限定されません。[ 22 ] [ 23 ]計算カーネルは、従来の言語でアルゴリズムを実装する際の内部ループ(暗黙の順次操作がないことを除く)、または内部反復子に渡されるコードにほぼ相当します。Microsoft はこれをDirectComputeとしてサポートしています
このプログラミングパラダイムはベクトルプロセッサによく適合します。バッチ内のカーネルの各呼び出しは独立しているという前提があり、データの並列実行が可能です。ただし、シナリオによっては、要素間の同期(相互依存的な作業)にアトミック操作が使用される場合もあります。個々の呼び出しにはインデックス(1次元または複数次元)が与えられ、そこからバッファデータの任意のアドレス指定(スキャッターギャザー操作を含む)を実行できます。ただし、重複しないという前提が守られる必要があります。
Vulkan APIは、言語およびマシンに依存しない方法でグラフィカルシェーダと計算カーネルの両方を記述するための中間表現SPIR-Vを提供します。その目的は、言語の進化を促進し、ユニファイドメモリアーキテクチャやヘテロジニアスシステムアーキテクチャなどのハードウェアの進歩に合わせて、GPUの計算能力をより自然に活用できるようにすることです。これにより、CPUとGPUのより緊密な連携が可能になります。
コード最適化の手段として、LLMによるカーネル生成の分野では多くの研究が行われてきました。スタンフォード大学のScaling Intelligence Labが開発したKernelBench [ 24 ]は、LLMが効率的なGPUカーネルを生成する能力を評価するためのフレームワークを提供しています。Cognitionは、効率的なCUDAカーネルを生成するためにKevin 32-B [ 25 ]を開発しました。これは現在、KernelBench上で最高性能のモデルとなっています。
シェーダーを作成するためのプログラミング言語はいくつかあり、どれを使用するかはターゲット環境によって異なります。OpenGLのシェーディング言語はGLSLで、Direct3DはHLSLを使用します。Appleデバイスで使用されるMetalフレームワークには、 Metal Shading Languageと呼ばれる独自のシェーディング言語があります
近年のグラフィックスAPIでは、シェーダーがエンドユーザーに配布される前に、中間言語であるSPIR-Vにコンパイルされることが増えています。この標準規格により、ターゲットプラットフォームに関わらず、より柔軟なシェーディング言語の選択が可能になります。[ 26 ] SPIR - VはVulkanとOpenGLで初めてサポートされ、Direct3Dでも採用されつつあります。[ 27 ]
Unity、Unreal Engine、Godotなどの最新のビデオゲーム開発プラットフォームには、コードを書かずにシェーダーを作成できるノードベースのエディターがますます多く搭載されています。代わりに、接続されたノードの有向グラフが表示され、さまざまなテクスチャ、マップ、数学関数を、拡散色、鏡面反射色と強度、粗さ/金属性、高さ、法線などの出力値に導くことができます。その後、グラフはシェーダーにコンパイルされます