レンダリングコードでID3D11InputLayoutを処理するのに最適な方法は?


7

directx11コードで入力レイアウトを処理するエレガントな方法を探しています。

EffectクラスとElementクラスがあるという問題があります。エフェクトクラスはシェーダーと同様の設定をカプセル化し、Elementクラスは描画可能なもの(3dモデル、lanscapeなど)を含みます

私の描画コードは、指定された効果を使用してデバイスシェーダーなどを設定し、次にElementのdraw関数を呼び出して、それに含まれる実際のジオメトリを描画します。

問題はこれです-どこかにD3D11InputLayoutを作成する必要があります。これは実際にはElementクラスに属します。これは、その要素が頂点レイアウトを表すためにどのように選択するかは、システムの他の部分のビジネスではないためです。ただし、オブジェクトを作成するために、APIはオブジェクトの描画に使用される頂点シェーダーの頂点シェーダーバイトコードを必要とします。directx9では簡単で、依存関係がなかったので、要素に独自の入力レイアウト構造を含め、影響を与えることなくそれらを設定できました。

しかし、要素は、それが描画されている効果について何も知っている必要はありません。それは単にレンダリング設定であり、要素はジオメトリを提供するために存在します。

そのため、保存する場所と、各描画呼び出しに対してInputLayoutを選択する方法が本当にわかりません。つまり、私は何かを働かせましたが、それは非常に醜いようです。

これは私が何か明白なものを逃したことを私に引き起こします、さもなければ、すべてのレンダリング設定をエフェクトに、要素にジオメトリを、そしてそれをすべて描画するサードパーティを持っている私のデザインはただ欠陥があります。

他の誰かがdirectx11で入力レイアウトをエレガントな方法でどのように処理するのか疑問に思っていますか?

回答:


3

これを処理する方法は、さまざまなタイプの入力レイアウト(VertexPositionNormal、VertexNormalUV ...)の束が簡単に作成/破棄できるように構造体でラップされているVertexFormat.hファイルがあることです。それは醜いIMOですが、かなりうまくいきます。現在サポートされていないレイアウトを使用するシェーダーがある場合、ファイルに新しい頂点フォーマットを追加するのは非常に簡単です。次に、必要なときにこれらのレイアウトを作成/破棄するVertexFormatManagerがあります。したがって、1つを作成する必要がある場合は、シェーダーバイトコードとvertexformat IDをマネージャーにフィードし、それを作成してプールに格納することで、各レイアウトが1つだけになるようにします。次に、ジオメトリを作成する必要があります。これを行うには、頂点クラスのIDを取得するジオメトリクラスにCreateメソッドがあります。create関数では、指定されたIDを使用して頂点フォーマットマネージャーにクエリを実行します。マネージャーは、入力レイアウトに含まれる要素のすべてのタイプにフラグが設定されたビットフィールドとして機能する整数を返します。だから私はこのようなものを持つことができました:

int bitfield = VertexFormatManager::QueryFormat( formatID );
if (bitfield & VertexFormat::NORMAL)
{
    // do somethings
}
if (bitfield & VertexFormat::UV)
{
    // do somethings
}
...

各入力レイアウト要素は、個別のID3D11Bufferに作成されます。これにより、任意のタイプの頂点フォーマットを指定してメッシュを構築できます。テクスチャのある立方体をレンダリングしたい場合、頂点の位置とUV座標が必要です。VertexPositionUV(例として)フォーマットIDをフィードすると、クエリは、POSITIONおよびUVフラグのセットビットを含むビットフィールドを返します。これにより、2つのID3D11Bufferが作成されます。1つは位置用、もう1つはUV座標用です。次に、これらの両方をパイプラインに接続してから描画します。これを行う利点は、シャドウマップなどを実行する場合に、頂点位置のバッファーセットのみを使用してメッシュをレンダリングできることです。

これの多くが非常に明確であるかどうかはわかりませんが、私は仕事中なので、今夜中に適切な書き込みを行います。それまでの間、私はこの質問を少し前に行って、この回答を得ました。それは良いことですが、特にDX11の場合は、今やっている方法のほうが理にかなっています。


これは非常に理にかなっています。包括的な回答をありがとうございます...したがって、描画コマンドごとに複数の頂点バッファーを使用しますか?
JohnB

はい、各要素(位置、法線、uv ...)のバッファーで使用します。レンダリングのタイプに応じて便利で、かなり柔軟性があります。それは確かにそれを行う唯一の方法ではありませんが、ほとんどのレンダリングルーチンIMHOでうまく機能します。ここにもう少し詳細と疑似コードを追加しました。pastebin.com / XfqS6N7gそれがお役に立てば幸いです。
dotminic

6

これに対する回答をありがとう、彼らは非常に役に立ちます。少し先に進んだので、自分の質問に回答を追加します。

結局、シェーダーを描画可能なアイテムから完全に分離したいのは間違いだと気づきました。それらは「実生活」で基本的に結合されているので、設計に含まれていることはおそらく問題ではありません。

たとえば、ウォーターシェーダーは、水を描画したい要素でのみ機能します。それだけが、必要なデータを提供できます。アニメーションモデルを描画するシェーダーは、アニメーションモデル要素でのみ機能します。アニメーションモデルでウォーターシェーダーを使用することを突然決めることができません。それは単に機能しません。

任意のシェーダーを読み込み、任意の描画可能な要素を操作できる抽象シェーダークラスを作成するのは誤りです。実際の使用方法は反映されていません。

代わりに、シェーダークラスAnimatedModelShader、WaterShader、LandscapeShaderを作成しました。これらはそれぞれ、異なる物理シェーダーファイルをロードするオプションを備えている可能性がありますが、ランドスケープシェーダーは常に同じ種類のデータ入力を必要とするため、必要に応じて常に同じ基本インターフェイスを備えています。

シェーダは今それ自身の入力レイアウトを作成するための責任があると、それはそう告げるどのように要素の用途は、それがそのシェーダを使用したい場合は、そのvertexTypeと呼ばれる公共のtypedefを持つことによって、それの頂点をレイアウトにそれを使用して要素を。

私のオリジナルデザインはまったくありませんが、結局のところ、2つの概念を分離したいという欲求は、とにかくあまり役​​に立ちませんでした。


3

私はdotminicと同様のことをしています-D3D11_INPUT_ELEMENT_DESCの配列を渡すルーチンがあります。これは、一致する入力シグネチャを持つ偽の頂点シェーダーを構築し、それをコンパイルして、レイアウトを作成し、最後に偽のシェーダーをリリースします。

はい、それは醜いです、そして、はい、それは私がそれを適切な方法で行った場合にそうでない場合よりもダニをより注意深くする必要があることを意味しますが、それらは現在よりきれいな分離と引き換えに受け入れる用意があるトレードオフです。


このアプローチから離れて、入力レイアウトと頂点シェーダーをより緊密に結合する方向に移動し、常に一緒に作成することに注意してください。私がここで与えるアプローチは、実際にそれらを切り離したい場合は、おそらくまだ問題ありません。
マクシムスMINIMUS

2

あなたは本当に心配する必要がないように私に見えます。CreateInputLayoutのMSDNドキュメントを一瞥した後、私はこれに遭遇しました:

input-layoutオブジェクトがシェーダーシグネチャから作成されると、input-layoutオブジェクトは、同じ入力シグネチャ(セマンティクスが含まれる)を持つ他のシェーダー再利用できます。これにより、同一の入力を持つ多くのシェーダーを操作するときに、入力レイアウトオブジェクトの作成を簡略化できます。(ビンゴ) input-layout宣言のデータ型がシェーダー入力シグネチャのデータ型と一致しない場合、CreateInputLayoutはコンパイル中に警告を生成します。警告は、レジスタから読み取られたときにデータが再解釈される可能性があるという事実に注意を促すためのものです。この警告を無視するか(意図的に再解釈する場合)、両方の宣言でデータ型を一致させて警告を排除できます。

http://msdn.microsoft.com/en-gb/library/windows/desktop/ff476512(v=vs.85).aspx

これにより、入力レイアウトを古いDX9頂点宣言とほぼ同じ方法で扱うことができるはずです。それぞれを初めて作成するときにシェーダーバイトコードを提供すると、DX11がそれらを検証できるようになります。そして明らかに、このステップを回避または失敗した場合に発生する最悪の事態は、無視しても安全と思われる実行時の警告です。

したがって、DX11は入力レイアウトとシェーダーの間に「依存関係を作成する」わけではありません...それは無料の検証を提供しています!:D


CreateInputLayout既存の入力レイアウトに対してシェーダーを検証するために使用することもできるので、それを使用するすべてのシェーダーを確認できます。これは、安全のため、少なくともデバッグビルドで行うことをお勧めします。
Nathan Reed

うーん、これを試みたときに、既存の入力レイアウトをパラメーターとして指定すると、古いポインターが上書きされるだけで、メモリリークが発生することがわかりました。残念なことに、個別の「検証」機能はありません。
Richard Copperwaite 2013年

申し訳ありませんが、「既存の入力レイアウト」とは、そのD3D11_INPUT_ELEMENT_DESC作成に使用されたの配列を意味しました。 これは、新しい入力レイアウトオブジェクトを作成せずに、別のシェーダーのバイトコードに対して検証できます。
Nathan Reed
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.