グラフベースのマテリアルシステムで、さまざまな入力および出力タイプをどのようにサポートできますか?


11

ここに画像の説明を入力してください

私のようなどのように材料系のまわりで私の頭をラップしようとしているこのこれは実装されています。これらの強力でユーザーフレンドリーなグラフのようなシステムは、プログラマーと非プログラマーの両方がシェーダーをすばやく作成できるようにする方法として比較的一般的であるようです。ただし、グラフィックスプログラミングの私の比較的限られた経験から、私はそれらがどのように機能するか完全にはわかりません。


バックグラウンド:

そのため、以前に単純な OpenGLレンダリングシステムをプログラミングしたことがある場合は、通常、手動で作成した静的GLSLファイルからシェーダーを読み込み、コンパイル、リンクするMaterialクラスを作成します。また、通常、このクラスを、GLSLの統一変数にアクセスするための単純なラッパーとして作成します。簡単な例として、基本的な頂点シェーダーとフラグメントシェーダーがあり、テクスチャを渡すための追加の均一なTexture2Dがあるとします。私のMaterialクラスは、これら2つのシェーダーをマテリアルに読み込んでコンパイルし、その時点から、そのシェーダーのTexture2Dユニフォームを読み書きするための単純なインターフェイスを公開します。

このシステムをもう少し柔軟にするために、私は通常、任意の名前/タイプのユニフォームを渡すことができるようにシステムを記述します[ie:SetUniform_Vec4( "AmbientColor"、colorVec4);]。これは均一な材料中に存在する場合、「colorVec4」と呼ばれる特定の4DベクトルにAmbientColorを均一に設定します。]

class Material
{
    private:
       int shaderID;
       string vertShaderPath;
       string fragSahderPath;

       void loadShaderFiles(); //load shaders from files at internal paths.
       void buildMaterial(); //link, compile, buffer with OpenGL, etc.      

    public:
        void SetGenericUniform( string uniformName, int param );
        void SetGenericUniform( string uniformName, float param );
        void SetGenericUniform( string uniformName, vec4 param );
        //overrides for various types, etc...

        int GetUniform( string uniformName );
        float GetUniform( string uniformName );
        vec4 GetUniform( string uniformName );
        //etc...

        //ctor, dtor, etc., omitted for clarity..
}

これは機能しますが、Materialクラスのクライアントは信仰だけでユニフォームにアクセスする必要があるため、悪いシステムのように感じられます。ユーザーは、強制されるため、各マテリアルオブジェクトにあるユニフォームに多少注意する必要があります。それらをGLSL名で渡します。1〜2人でシステムを操作する場合は、それほど大きな問題ではありませんが、このシステムが非常に適切に拡張できるとは思えません。OpenGLレンダリングシステムをプログラミングする次の試みを行う前に、平準化したいと思います。少しアップ。


質問:

私は今のところそこにいるので、私は他のレンダリングエンジンがどのようにマテリアルシステムを処理するかを研究しようと努めてきました。

このノードベースのアプローチは優れており、最新のエンジンとツールでユーザーフレンドリーなマテリアルシステムを作成するための非常に一般的なシステムのようです。私が言うことができることから、それらはグラフデータ構造に基づいており、各ノードはマテリアルのシェーダーの側面を表し、各パスはそれらの間のある種の関係を表します。

私が言うことができることから、そのようなシステムの実装は、さまざまなサブクラス(TextureNode、FloatNode、LerpNodeなど)を持つMaterialNodeクラスのように単純です。各MaterialNodeサブクラスにはMaterialConnectionsがあります。

class MaterialConnection
{
    MatNode_Out * fromNode;
    MatNode_In * toNode;
}

class LerpNode : MaterialNode
{
    MatNode_In x;
    MatNode_In y;
    MatNode_In alpha;

    MatNode_Out result;
}

これは非常に基本的な考え方ですが、このシステムのいくつかの側面がどのように機能するかについては、少し不確かです。

1.)Unreal Engine 4が使用するさまざまな「マテリアル式」(ノード)を見ると、それぞれにさまざまなタイプの入力および出力接続があることがわかります。一部のノードはフロートを出力し、一部の出力ベクター2、一部の出力ベクター4などを出力します。さまざまな入力および出力タイプをサポートできるように、上記のノードと接続を改善するにはどうすればよいですか?MatNode_OutをMatNode_Out_FloatおよびMatNode_Out_Vec4(など)でサブクラス化することは賢明な選択でしょうか?

2.)最後に、この種のシステムはGLSLシェーダーとどのように関係していますか?UE4をもう一度見ると(上でリンクされている他のシステムについても同様)、ユーザーは最終的に、いくつかのマテリアルノードを、シェーダーパラメーター(基本色、金属、光沢、放射率など)を表すさまざまなパラメーターを持つ大きなノードに接続する必要があります。 。私の当初の想定では、UE4にはさまざまなユニフォームを備えたハードコーディングされたある種の「マスターシェーダー」があり、ユーザーが「マテリアル」で行うすべてのことは、ノードを「マスターノード」。

ただし、UE4のドキュメントには次のように記載されています。

「各ノードには、特定のタスクを実行するように指定されたHLSLコードのスニペットが含まれています。これは、マテリアルを構築するときに、ビジュアルスクリプトを使用してHLSLコードを作成することを意味します。」

それが本当なら、このシステムは実際のシェーダースクリプトを生成しますか?これはどのように機能しますか?


1
あなたの質問に関連して:gameangst.com/
p=

回答:


10

UE4の特定のケースに関する知識はほとんどなく、むしろ一般的な手法について、私の知る限りの知識に答えようと思います。

グラフベースの資料は、自分でコードを書くのと同じくらい多くのプログラミングです。それは、コードの背景がない人にとっては、そのようには感じられないため、一見簡単に見えるようになっています。したがって、デザイナーが「追加」ノードをリンクするとき、彼は基本的にadd(value1、value2)を記述し、出力を他のものにリンクします。これは、各ノードがHLSLコードを生成することを意味しています。HLSLコードは、関数呼び出しまたは単純な命令のいずれかです。

結局、マテリアルグラフの使用は、いくつかの一般的な有用なことを行う事前定義された関数のライブラリを使用して生のシェーダーをプログラミングするようなものであり、それもUE4が行うことです。シェーダーコンパイラーが取得し、該当する場合は最終的なシェーダーソースに挿入するシェーダーコードのライブラリがあります。

UE4の場合、HLSLに変換されたと主張する場合、HLSLバイトコードをGLSLバイトコードに変換できる変換ツールを使用しているので、GLプラットフォームで使用できます。ただし、他のライブラリには、グラフを読み取って必要なシェーディング言語ソースを直接生成する複数のシェーダーコンパイラーしかありません。

マテリアルグラフは、プラットフォームの詳細から抽象化し、アートディレクションの観点から重要なことに焦点を当てるのにも良い方法です。言語にバインドされておらず、はるかに高いレベルであるため、ターゲットプラットフォーム向けに最適化し、ライト処理などの他のコードをシェーダーに動的に注入するのが簡単です。

1)質問に直接回答するには、このようなシステムを設計するためのデータ駆動型のアプローチが必要です。非常に単純な構造で定義でき、さらにはテキストファイルでも定義できるフラットフォーマットを見つけます。基本的に、各グラフはノードの配列であり、タイプ、入力と出力のセットが必要です。これらの各フィールドには、グラフの接続が明確になるようにローカルlink_idが必要です。また、これらの各フィールドには、フィールドがサポートするもの(たとえば、サポートされるデータ型の範囲)に対する追加の構成を含めることができます。

このアプローチを使用すると、ノードのフィールドを(float | double)として簡単に定義し、クラスの階層やオーバーエンジニアリングを行わずに、接続から型を推測したり、型を強制したりできます。このグラフのデータ構造を必要に応じて固定または柔軟に設計するのはあなた次第です。必要なのは、コードジェネレーターがあいまいさを持たないように十分な情報が含まれているため、必要な処理を誤って処理する可能性があることだけです。重要なことは、基本的なデータ構造レベルでは、それを柔軟に保ち、マテリアルのみを定義するタスクの解決に集中することです。

「材料を定義する」と言うとき、私はジオメトリ自体が提供するものを超えて、メッシュサーフェスを定義することについて非常に具体的に言及しています。これには、追加の頂点属性を使用してサーフェスのアスペクトを構成し、ハイトマップでそれに変位を追加し、ピクセルごとの法線で法線を乱す、物理ベースのパラメーターを変更する、BRDFを変更するなどが含まれます。HDR、トーンマッピング、スキニングアニメーション、ライトの処理、またはシェーダーで行われる他の多くのことのような他のものを記述したくない場合。

2)次に、このデータ構造をトラバースし、その情報を読み取ることによって一連の変数を組み立て、事前に作成された関数を使用してそれらをリンクし、照明やその他の効果を計算するコードを挿入するのは、レンダラーのシェーダージェネレーター次第です。シェーダーは異なるグラフィックAPIだけでなく、異なるレンダラー間でも異なることを覚えておいてください(遅延レンダリング、タイルベース、およびフォワードレンダラーはすべて、機能するために異なるシェーダーを必要とします)。このようなマテリアルシステムを使用すると、厄介なものから抽象化できます低レベルのレイヤーであり、表面の説明にのみ焦点を当てています。

UE4について、彼らはあなたが言及するその最終的な出力ノードに関するもののリストを思いつきました、それらは古いものと現代のゲームの表面の99%を説明していると思います。彼らは何十年にもわたってこのパラメータセットを開発し、アンリアルエンジンがこれまでに生み出した非常に多くのゲームでそれを証明しました。したがって、unrealと同じ方法で問題がなければ、問題はありません。

まとめると、各グラフを処理するだけの.materialファイルをお勧めします。開発中は、デバッグするテキストベースの形式が含まれている可能性があり、リリース用にパッケージ化またはバイナリにコンパイルされます。各.materialは、SQLデータベースのように、NノードとN接続で構成されます。各ノードにはN個のフィールドがあり、入力と出力の場合、タイプが推定される場合など、受け入れられるタイプの名前といくつかのフラグが含まれます。ロードされたマテリアルを保持するランタイムデータ構造は、フラットでシンプルなので、エディターは簡単にそれを適応させ、ファイルに保存できます。

そして、最後のシェーダー生成のために実際の重い作業を残します。これは、実行するのが本当に難しい部分です。美しい部分は、マテリアルがレンダリングプラットフォームにとらわれないままであるということです。理論的には、適切なシェーディング言語でマテリアルを表現する限り、あらゆるレンダリング手法とAPIで動作します。

追加の詳細や私の回答の修正が必要な場合は、私にお知らせください。すべてのテキストの概要を失いました。


このような精巧で優れた答えを書いてくれてありがとう。ここからどこに行けばいいのか、すごくいい気分です!ありがとう!
MrKatSwordfish

1
問題がありません。さらにヘルプが必要な場合は、遠慮なく私にメッセージを送ってください。私は実際には自分のツールと同等のものに取り組んでいるので、考えを交換したい場合は、ゲストになってください!良い午後をお過ごしください:D
Grimshaw
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.