2017-01-06 11 views
0

私のゲームでシェーダを整理するのに助けが必要です。HLSL - シェーダとテクニックを整理し、紛失しないようにする

テクスチャリングとライティングには、頂点シェーダとピクセルシェーダが使用されます。いくつかのオブジェクトにはテクスチャが施されていますが、他に色付けされているものもあります。ライトマップ、拡散照明、鏡面照明、一部のオブジェクトだけのシャドウ生成などがあります。

私の最初のアイデアは、それはパラメータに基づいてすべてを扱うことができます。これは維持するのが賢明でした(考えを与える擬似コード)。

float4 PixelShaderFunction(input) 
{ 
    if (UseTexture) 
     objectColor = tex2D(...) 
    else 
     objectColor = ObjectColor; 

    if (UseAmbient) 
     ... 

    if (UseDiffuseLight) 
     ... 

    // etc... 
} 

パフォーマンスは低かったです。

次に、ifの分岐を避けるために、複数の手法を作成しました。すべての手法では、特定のオブジェクトに必要なものだけが使用されます。オブジェクトが影を受け入れない場合、そこにコードはありません。鏡面光を出さない場合、コードはありません。プラス私は機能に共通の機能をグループ化した。このように(実際のコードは現在):

float4 Textured_PixelShader(Textured_VsOut input) : COLOR0 
{ 
    float4 lightLevel = 0; 
    float4 objectColor = GetObjColorTex(input.TexUV); 

    // calculate main table lighting 
    PsAddTableLight(input.WorldPosition, input.Normal, lightLevel); 

    // calculate cloth lighting 
    PsAddClothLight(input.WorldPosition, input.Normal, lightLevel); 

    // calculate fill light - do we need it? 
    PsAddFillLight(input.Normal, lightLevel); 

    // add ambient light 
    PsAddAmbientLight(lightLevel); 

    // apply light 
    PsApplyDiffuseLight(lightLevel, objectColor); 

    // add speculars 
    PsAddSpecularBlurred(input.WorldPosition, input.Normal, objectColor); 

    // final work 
    return PsFinalize(objectColor); 
} 

パフォーマンスの向上は大きかった。

しかし、私はこのシェーダの維持に迷っています。 2日おきにtexture + lightmap + this_kind_of_shadow + specularのような組み合わせがまだないので、私は新しい技術を追加する必要があります。それらの名前の中にはこのような名前が付いているものもあれば、そのような組み合わせを持つオブジェクトが1つしかないため、他の名前は使用されているオブジェクトによって名前が付けられます。そしてそれは混乱になっています。

私は、2つの質問があります:私はそれらのifステートメントを持つことができないのはなぜ

  • を?条件付き実行がGPUにどのような影響を与えるかについて多くのことを読んでいますが、ifはすべてのレンダリングされたピクセル(またはverts)の値が同じシェーダパラメータにのみ依存します。なぜ彼らは速くないのですか?私は本当にそれらが恋しいです。
  • このコードを異なるシェーダ/技術/ファイルに分割する最も良い方法は何ですか?良い基準やルールはありますか?

答えて

0

if文を使用できないのはなぜですか?条件付き実行がGPUにどのような影響を与えるかについて多くのことを読んでいますが、ifはすべてのレンダリングされたピクセル(またはverts)の値が同じシェーダパラメータにのみ依存します。なぜ彼らは速くないのですか?私は本当にそれらが恋しいです。

思考:

主な問題は、コンパイラが唯一のブール式ではなく、あなたの変数がシェーダ定数であることを意味的な情報を見ること、ここにあります。これは特別な場合に過ぎず、たとえば、シェイダー定数だけを使用する関数でより洗練されたブール式を使用すると複雑になります。私はシェイダーコンパイラの開発者が頭を悩ませることを避けるために、それが一般的であればそれを許すことを選択すると思います。

事実:HLSL-Documentation for ifに述べたように

、2つのモードがありますflattenまたはbranchのいずれか、場合。 flattenを指定すると、コンパイラはifの両側をロールアウトします。最初に計算されるので、結果は右側から取得されます。branchでは、ブール値が最初に評価されるため、右側のみが実行されますが、このモードは、tex2Dのようなグラデーション関数を使用しない場合にのみ使用できます。これは、ネイバーフラグメントに依存しているためです。各断片をスキップしてはならない。あなたの場合、私はあなたがそのような関数を使用していることをかなり確信しているので、コンパイラはあなたのifに対してflattenを選択し、完全に実行され、遅いシェーダになります。

このコードを異なるシェーダ/技術/ファイルに分割する最も良い方法は何ですか。良い基準やルールはありますか?

私が知る限り、良い基準はありませんが、UnityやUnrealのようなエンジンは、シェイダーをどのように管理しているか、より深い外観を持つ良いスポットです。最後にUnrealを調べると、必要になると各シェーダが動的に生成されたので、シェーダコードはシェイダービルダーから生成され、コンパイルされます。

私自身の小さなエンジンでは、私はあなたと同様のアプローチを使用しましたが、動的分岐の代わりに、プリプロセッサーディレクティブ#if, #elif, #else, and #endifを使用しています。エンジンが必要なシェイダーに遭遇した場合は、右の定義を設定して、D3DCompileでオンザフライでコンパイルします。吃音を防ぐために、コンパイルされたシェーダをディスクに保存します。コンパイルする前に、シェーダが以前にコンパイルされていればそれを探しています。

+0

残念ながら、あなたの方法(動的コンパイル)に従うことができないようです。モノゲームはそれをサポートしていないので、たとえそれが一部のプラットフォームのみに限定されているとしてもです。しかし、これは私が受け入れられたと印を付ける偉大で価値ある答えです - ありがとう。 – Arek

+0

なぜ、 'if'が遅いのか - 私はまだ分かりませんが、すぐにパフォーマンスの低下を確認するためにいくつかの再テストを行います。すべての情報源によれば、 'if'条件はレンダリングされるすべてのものに対して同じであるため、速くなければなりません。 – Arek

関連する問題