web-dev-qa-db-ja.com

GLSLの頂点シェーダー属性マッピング

小さなレンダリングエンジンをGLSLシェーダーでコーディングしています。

各メッシュ(ウェル、サブメッシュ)には、1つの大きなVBOとMaterialIDへの多数の頂点ストリーム(位置、法線、テクスチャ、接線など)があります。

各マテリアルには、テクスチャとプロパティのセットがあります(例:鏡面色、拡散色、色テクスチャ、法線マップなど)。

次に、GLSLシェーダーがあり、ユニフォームと属性があります。まあ言ってみれば:

uniform vec3 DiffuseColor;
uniform sampler2D NormalMapTexture;
attribute vec3 Position;
attribute vec2 TexCoord;

GLSLシェーダーが属性とユニフォームのストリームマッピング(セマンティクス)を定義し、頂点ストリームを適切な属性にバインドする方法を設計しようとして少し行き詰まっています。

メッシュに言うことの何か: "属性" Position "に位置ストリームを置き、" TexCoord "にテクスチャ座標を置きます。また、マテリアルの拡散色を" DiffuseColor "に、マテリアルの2番目のテクスチャを" NormalMapTexture "に入れます。

現時点では、属性にハードコーディングされた名前を使用しており(つまり、頂点posは常に "Position"など)、各ユニフォームと属性名をチェックして、シェーダーがそれを何に使用しているかを理解しています。

「頂点宣言」を作成する方法を探していると思いますが、ユニフォームとテクスチャも含まれています。

だから私は人々が大規模なレンダリングエンジンでこれをどのように行うのかと思っています。

編集:

提案された方法の要約:

1。属性/均一なセマンティクスは変数の名前で指定されます(現在私がやっていること)可能な各属性に事前定義された名前を使用します。GLSLバインダーは各属性の名前を照会し、変数の名前に基づいて頂点配列をリンクします。

//global static variable

semantics (name,normalize,offset) = {"Position",false,0} {"Normal",true,1},{"TextureUV,false,2}

 ...when linking
for (int index=0;index<allAttribs;index++)
{
   glGetActiveAttrib(program,index,bufSize,length,size[index],type[index],name);      
   semantics[index]= GetSemanticsFromGlobalHardCodedList(name);
} 
... when binding vertex arrays for render
 for (int index=0;index<allAttribs;index++)
{
    glVertexAttribPointer(index,size[index],type[index],semantics[index]->normalized,bufferStride,semantics[index]->offset);

}  

2。各セマンティックの事前定義された場所

GLSLバインダーは、常に頂点配列を同じ場所にバインドします。一致する適切な名前を使用するかどうかは、シェーダー次第です。 (これは方法1に酷似しているように見えますが、私が誤解しない限り、これは、シェーダーが頂点データを消費していなくても、利用可能なすべての頂点データをバインドすることを意味します)

.. when linking the program...
glBindAttribLocation(prog, 0, "mg_Position");
glBindAttribLocation(prog, 1, "mg_Color");
glBindAttribLocation(prog, 2, "mg_Normal");

。マテリアル、エンジングローバル、レンダラー、メッシュから利用可能な属性の辞書

アクティブなマテリアル、エンジングローバル、現在のレンダラー、現在のシーンノードによって公開されている使用可能な属性のリストを保持します。

例えば:

 Material has (uniformName,value) =  {"ambientColor", (1.0,1.0,1.0)}, {"diffuseColor",(0.2,0.2,0.2)}
 Mesh has (attributeName,offset) = {"Position",0,},{"Normals",1},{"BumpBlendUV",2}

次にシェーダーで:

 uniform vec3 ambientColor,diffuseColo;
 attribute vec3 Position;

頂点データをシェーダーにバインドすると、GLSLバインダーは属性をループし、ディクショナリーで見つかった(またはそうではない?)にバインドします。

 for (int index=0;index<allAttribs;index++)
    {
       glGetActiveAttrib(program,index,bufSize,length,size[index],type[index],name);      
      semantics[index] = Mesh->GetAttributeSemantics(name);
}

ユニフォームの場合も同様で、アクティブなマテリアルとグローバルのみをクエリします。

38
Radu094

属性:

メッシュには多数のデータストリームがあります。各ストリームについて、次の情報を保持できます:(name、type、data)。

リンクすると、アクティブな属性をGLSLプログラムに照会し、このプログラムの属性ディクショナリを形成できます。ここの各要素は(name、type)です。

指定されたGLSLプログラムを使用してメッシュを描画する場合、プログラムの属性ディクショナリを調べ、対応するメッシュストリームをバインドします(または、不整合の場合はエラーを報告します)。

ユニフォーム:

シェーダーパラメーターディクショナリを(name、type、data link)のセットとします。通常、次の辞書を使用できます。

  • マテリアル(拡散、鏡面、光沢など)-マテリアルから取得
  • エンジン(カメラ、モデル、ライト、タイマーなど)-エンジンシングルトンから取得(グローバル)
  • レンダリング(シェーダークリエーターに関連するカスタムパラメーター:SSAO半径、ブラー量など)-シェーダークリエータークラス(レンダー)によってのみ提供されます

リンクした後、GLSLプログラムには、独自のディクショナリに次の要素形式を設定するためのパラメータディクショナリのセットが与えられます:(location、type、data link)。この設定は、アクティブなユニフォームのリストをクエリして、(name、type)ペアを辞書のペアと照合することによって行われます。

結論:このメソッドを使用すると、エンジンにハードコードされた名前/セマンティクスなしで、カスタム頂点属性とシェーダーのユニフォームを渡すことができます。基本的に、ローダーとレンダーだけが特定のセマンティクスについて知っています。

  • ローダーはメッシュデータストリームの宣言とマテリアルディクショナリに入力します。
  • Renderは、名前を認識し、追加のパラメーターを提供し、描画に使用する適切なメッシュを選択するシェーダーを使用します。
16
kvark

私の経験から、OpenGLは属性やユニフォームのセマンティクスの概念を定義していません。

できることは、yourセマンティクスをOpenGL変数にマッピングする独自の方法を定義し、これらの変数について制御できる唯一のパラメーターを使用することです:それらのlocation

プラットフォームの問題に制約されていない場合は、「新しい」 GL_ARB_explicit_attrib_location (誤っていない場合はOpenGL 3.3のコア)を使用して、シェーダーが目的の場所を明示的に表現できるようにすることができますどの属性。このようにして、どの属性の場所にバインドするデータをハードコーディング(または構成)し、コンパイル後にシェーダーの場所をクエリできます。この機能はまだ成熟していないようで、おそらくさまざまなドライバのバグの影響を受ける可能性があります。

もう1つの方法は、glBindAttribLocationを使用して属性の場所をバインドすることです。そのためには、バインドする属性の名前と、それらに割り当てる場所を知っている必要があります。

シェーダーで使用されている名前を見つけるには、次の方法があります。

  • シェーダーにアクティブな属性を問い合わせる
  • シェーダーのソースコードを解析して自分で見つけます

GLSLの解析方法を使用することはお勧めしません(ただし、十分に単純なコンテキストの場合はニーズに合うかもしれません):パーサーはプリプロセッサーによって簡単に無効にできます。シェーダーコードがやや複雑になると仮定すると、#includes、#defines、#ifdefなどの使用を開始したい場合があります。堅牢な解析では、設定するのにかなりの負荷がかかる可能性がある堅牢なプリプロセッサがあると想定します。

とにかく、アクティブな属性名を使用して、それらに場所(および/またはセマンティクス)を割り当てる必要があります。これを行うには、ユースケースを1つにします。

私たちのエンジンでは、定義済みの名前の場所を次のような特定の値にハードコードします。

glBindAttribLocation(prog, 0, "mg_Position");
glBindAttribLocation(prog, 1, "mg_Color");
glBindAttribLocation(prog, 2, "mg_Normal");
...

その後、事前定義された属性のセマンティクスに準拠するかどうかは、シェーダー作成者次第です。

私の知る限り、それは物事を行うための最も一般的な方法です [〜#〜] ogre [〜#〜] たとえば、それを使用します。それはロケット科学ではありませんが、実際にはうまく機能します。

なんらかのコントロールを追加したい場合は、シェーダーベースでセマンティクスを定義するためのAPIを提供できます。おそらく、この説明を追加ファイルに記述して、簡単に解析でき、シェーダーのソースコードの近くに配置できます。

「新しい」拡張機能を使用すると、GLSLユニフォームブロックをアプリケーションと互換性のあるメモリレイアウトに強制できることを除いて、状況がほとんど同じであるユニフォームには入りません。

私はこれらすべてに満足しているわけではないので、矛盾する情報があれば嬉しいです:)

8
rotoglup

実際にGLSL自体を解析することを検討する必要があるかもしれません。

ユニフォーム/属性宣言の構文はかなり単純です。 uniformまたはattributeで始まる行を探し、タイプと名前を取得し、文字列を使用してC++ APIを公開する小さな手動パーサーを思いつくことができます。これにより、名前をハードコーディングする手間が省けます。手動解析で手を汚したくない場合は、 Spirit のようなものを2つ使用するとうまくいきます。
おそらくGLSLを完全に解析する必要はないので、実際の意味を変える可能性のある減速では、何もおかしくないことを確認する必要があります。頭に浮かぶのは、GLSLのマクロを使用した条件付きコンパイルです。

3
shoosh