この回答を複数の部分に分割しています。
ARB_separate_shader_objects の目的は何ですか
この機能の目的は、頂点/フラグメント/ジオメトリ/テッセレーション シェーダーを簡単に組み合わせることができるようにすることです。
現在、すべてのシェーダー ステージを 1 つのモノリシック プログラムにリンクする必要があります。したがって、2 つの異なるフラグメント シェーダーで同じ頂点シェーダー コードを使用することができます。しかし、これにより 2 つの異なるプログラムが作成されます。
各プログラムには、独自のユニフォーム セットとその他の状態があります。つまり、頂点シェーダーで均一なデータを変更したい場合は、両方のプログラムで変更する必要があります。それぞれで使用する必要glGetUniformLocation
があります(場所が異なる可能性があるため)。次に、それぞれに個別に値を設定する必要があります。
それは大きな苦痛であり、非常に不必要です。個別のシェーダーを使用すれば、その必要はありません。頂点シェーダーのみを含むプログラムと、2 つのフラグメント シェーダーを含む 2 つのプログラムがあります。glGetUniformLocation
頂点シェーダー ユニフォームの変更に 2 回の呼び出しは必要ありません。実際、頂点シェーダーは 1 つしかないため、データをキャッシュする方が簡単です。
また、シェーダーの組み合わせの組み合わせの爆発を扱います。
単純な剛体変換を行う頂点シェーダーがあるとします。モデルからカメラへのマトリックスとカメラからクリップへのマトリックスを使用します。たぶん、法線のマトリックスも。そして、いくつかのテクスチャからサンプリングし、法線に基づいていくつかの照明計算を行い、色を返すフラグメント シェーダーがあります。
ここで、追加のライティングとマテリアル パラメータを使用する別のフラグメント シェーダを追加するとします。頂点シェーダーからの新しい入力はなく (新しいテクスチャ座標などはありません)、新しいユニフォームだけです。おそらく、頂点シェーダーが関与していない投影照明用です。なんでもいい。
ここで、頂点加重スキニングを行う新しい頂点シェーダーを追加するとします。古い頂点シェーダーと同じ出力を提供しますが、スキニング用のユニフォームと入力ウェイトが多数あります。
これにより、2 つの頂点シェーダーと 2 つのフラグメント シェーダーが得られます。合計 4 つのプログラムの組み合わせ。
互換性のあるフラグメント シェーダーを 2 つ追加するとどうなりますか? 8つの組み合わせが得られます。3 つの頂点シェーダーと 10 のフラグメント シェーダーがある場合、合計30のプログラムの組み合わせがあります。
個別のシェーダーでは、3 つの頂点シェーダーと 10 のフラグメント シェーダーには 30 のプログラム パイプライン オブジェクトが必要ですが、 13のプログラム オブジェクトしか必要ありません。これは、非分離の場合よりも 50% 以上少ないプログラム オブジェクトです。
引用文が間違っている理由
さて、この行 [...] は不思議に思います。
不思議に思うはずです。いくつかの点で間違っています。例えば:
VAO は、バッファー データ、頂点レイアウト データ、および GLSL プログラム入力データをリンクするコンテナー オブジェクトです。
いいえ、違います。頂点データを提供するバッファ オブジェクトを、そのデータの頂点フォーマットに関連付けます。また、どの頂点属性インデックスに移動するかを指定します。ただし、これが「GLSL プログラムの入力データ」とどの程度密接に結合されているかは、完全にあなた次第です。
専用のソフトウェア設計がなければ、これは、オブジェクトのマテリアル (新しいフラグメント シェーダー) を変更するときに、別の VAO が必要になることを意味します...
この行が「専用のソフトウェア設計」と「合理的なプログラミングの実践」を同一視しない限り、これはまったくナンセンスです。
これが私の言いたいことです。頂点データを設定するときに次のようなことを行うサンプル コードをオンラインで見ることができます。
glBindBuffer(GL_ARRAY_BUFFER, buffer_object);
glEnableVertexAttribArray(glGetAttribLocation(prog, "position"));
glVertexAttribPointer(glGetAttribLocation(prog, "position"), ...);
これには専門用語があります:ひどいコードです。これを行う唯一の理由は、 で指定されたシェーダーprog
が何らかの形で直接制御されていない場合です。もしそうなら...どうやってそれprog
が「位置」という名前の属性を持っていることを知っていますか?
シェーダーの合理的なプログラミング方法は、規則を使用することです。prog
これで、「位置」という名前の属性があることがわかります。しかし、すべてのプログラムに「位置」という名前の属性があることがわかっている場合は、さらに一歩進んでみませんか? プログラムをリンクするときは、次のようにします。
GLuint prog = glCreateProgram();
glAttachShader(prog, ...); //Repeat as needed.
glBindAttribLocation(prog, 0, "position");
結局のところ、このプログラムには「位置」という名前の属性が必要であることがわかっています。後でその場所を取得すると、それを想定することになります。したがって、仲介者を切り取り、使用する場所をOpenGL に伝えます。
このように、使用する必要はありませんglGetAttribLocation
; 「位置」を意味する場合は、0 を使用してください。
prog
「位置」という名前の属性がない場合でも、これは正常にリンクされます。存在しない属性の場所をバインドしても、OpenGL は気にしません。したがって、一連のglBindAttribLocation
呼び出しを、作成するすべてのプログラムに問題なく適用できます。実際、属性名には複数の規則を使用できます。いずれかのセットに固執する限り、問題ありません。
glBindAttribLocation
さらに良いことに、それをシェーダーに貼り付けて、ソリューションをまったく気にしないでください。
#version 330
layout(location = 0) in vec4 position;
つまり、属性の場所には常に規則を使用してください。プログラムで見た場合はglGetAttribLocation
、コードの匂いだと考えてください。そうすれば、VAO は規則に反して単純に記述されているため、任意のプログラムに任意の VAO を使用できます。
規則を持つことが「専用のソフトウェア設計」にどのように等しいかはわかりませんが、その行も書きませんでした。