私は考えていました:
アプリケーションのすべてのオブジェクトに適用されるメイン シェーダーがあり、投影、変換、配置、カラーリングなどに使用されます。
また、各オブジェクトには、追加のもの用の独自の追加シェーダーがあります。たとえば、水のオブジェクトには追加のシェーダーが必要です。
しかし、問題があります。2 つ以上のシェーダーを 1 つのオブジェクトに適用するにはどうすればよいでしょうか? メイン シェーダー + オブジェクト独自のシェーダーを適用する必要があるためです。
OpenGL (または Direct3D!) で各頂点 / フラグメント / あらゆる段階で複数のシェーダーを使用できるようになれば本当に素晴らしいことですが、残念ながら既存のシステムでは行き詰っています。
一連の GLSL 関数を作成したとします。モデルビュー変換を適用したり、テクスチャ座標を次のステージにコピーしたりするなど、すべてのオブジェクトに汎用的なものもあります。水や岩など、特定のクラスのオブジェクトに固有のものもあります。
次に書くのはubershaderです。これは、頂点/フラグメント/どの段階でも main() 関数がこれらすべての関数を呼び出す以外に何もしないプログラムです。これは、より専門的なプログラムを生成するためのテンプレートまたはプロトタイプです。
最も一般的な方法は、main() 内の関数呼び出しの周りにプリプロセッサと多くの #ifdef を使用することです。たぶん、#defines なしでコンパイルすると、標準の変換とグーロー シェーディングが得られます。#define WATER を追加して水の効果を取得し、#define DISTORT をある種の自由形式の変形アルゴリズムに追加します。どちらも、自由形式の変形した水が必要な場合、#define FOG を追加して霧の効果を追加します...
実行時に #define 文字列を生成して glCompileShader に渡すことができるため、ubershader ソースの複数のコピーを用意する必要さえありません。
最終的には、レンダリングの種類ごとに 1 つずつ、多数のシェーダー プログラムが作成されます。なんらかの理由で 1 つのプログラムだけにしたい場合は、新しいシステムで GLSL サブルーチンを使用して同様のことを行うことができます。
これらは基本的に、ユニフォームのように設定できる GLSL の関数ポインターです。これで、ubershader には、main() 関数で 1、2、... 関数ポインター呼び出しが含まれています。あなたのプログラムは、#1 を標準の変換に、#2 を岩/水/その他に、#3 を霧に設定するだけです...ステージを使用したくない場合は、NOP 関数を用意するだけです。割当。
これには 1 つのプログラムしか使用できないという利点がありますが、任意のポインターが同じ関数プロトタイプを使用する必要があるため、#define アプローチほど柔軟ではありません。また、WATER を複数のシェーダーで処理する必要があると言うと、より多くの作業が必要になります。これは、1 つの #define だけでなく、すべてのシェーダーで関数ポインターを設定することを覚えておく必要があるためです。
お役に立てれば。