38

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

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

各マテリアルには一連のテクスチャとプロパティがあります (例: スペキュラー カラー、ディフューズ カラー、カラー テクスチャ、法線マップなど)。

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

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

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

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

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

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

だから、大規模なレンダリングエンジンでこれをどのように行うのか疑問に思っています。

編集:

提案された方法の要約:

1. 属性/Uniform セマンティックは、変数の名前によって与えられます (私が今行っていること) 可能な属性ごとに定義済みの名前を使用します。変数の名前:

//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");

3. マテリアル、エンジン グローバル、レンダラー、メッシュから利用可能な属性のディクショナリ

アクティブなマテリアル、エンジン グローバル、現在のレンダラー、および現在のシーン ノードによってパブリッシュされた使用可能な属性のリストを維持します。

例えば:

 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);
}

ユニフォームと同じように、アクティブなマテリアルとグローバルのみをクエリします。

4

3 に答える 3

16

属性:

メッシュには多数のデータ ストリームがあります。ストリームごとに、次の情報を保持できます: (名前、タイプ、データ)。

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

指定した GLSL プログラムでメッシュを描画するときは、プログラムの属性ディクショナリを調べて、対応するメッシュ ストリームをバインドします (矛盾がある場合はエラーを報告します)。

ユニフォーム:

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

  • マテリアル (ディフューズ、スペキュラ、シャイネスなど) - マテリアルから取得
  • エンジン (カメラ、モデル、ライト、タイマーなど) - エンジン シングルトンから取得 (グローバル)
  • Render (シェーダー クリエーターに関連するカスタム パラメーター: SSAO 半径、ぼかし量など) - シェーダー クリエーター クラス (render) によってのみ提供されます。

リンク後、GLSL プログラムには一連のパラメータ ディクショナリが与えられ、独自のディクショナリに次の要素形式 ( location, type, data link ) を入力します。このポピュレーションは、アクティブなユニフォームのリストを照会し、( name, type ) ペアを辞書内のものと照合することによって行われます。

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

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

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

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

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

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

シェーダーで使用されている名前を見つけるには、次のことができます。

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

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

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

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

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

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

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

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

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

私自身はこれに満足していないので、矛盾した情報があれば嬉しいです:)

于 2011-02-07T20:00:32.033 に答える
3

GLSL 自体を実際に解析することを検討することをお勧めします。

統一/属性宣言の構文は非常に単純です。uniformまたはattributeで始まる行を探し、型と名前を取得し、文字列を使用して C++ API を公開する小さな手動パーサーを考え出すことができます。これにより、名前をハードコードする手間が省けます。手動の解析で手を汚したくない場合は、Spiritのいくつかのようなものでうまくいくでしょう。
おそらく GLSL を完全に解析したくないので、実際の意味を変更する可能性のある減速でおかしなことをしないようにする必要があります。頭に浮かぶ 1 つの複雑さは、GLSL のマクロを使用した条件付きコンパイルです。

于 2011-02-07T09:05:11.740 に答える