シェーダー開発

シェーダーを記述するプログラマ向けのグラフィックス情報

クイック スタート

シェーダーで作業をする場合、r.ShaderDevelopmentMode を必ず 1 に設定して有効にしてください。最も簡単な方法は、ConsoleVariables.ini を編集して、ロード時に毎回有効になるようにします。これで、エラー時のリトライ、シェーダー開発関連のログ、および警告が有効になります。

recompileshaders changed コマンドを実行する Ctrl+Shift+. を使用します。このコマンドの実行は、Unreal Shader (.usf) ファイルへの変更を保存した後に行います。

多くのシェーダーに含まれるファイル (例、common.usf) を変更した場合は再コンパイルの時間がしばらくかかることがあります。マテリアルでイタレーションする場合、マテリアルに若干の変更を加えて (例、ノードの移動)、マテリアル エディタで 'Apply' を使用することでマテリアルの再コンパイルをトリガーできます。

シェーダーとマテリアル

グローバル シェーダー

グローバル シェーダーは、(フルスクリーン クアッドのように) 固定ジオメトリで機能するシェーダーであり、マテリアルとのインターフェースを必要としません。例としては、シャドウのフィルタリングやポストプロセスがあります。任意のグローバル シェーダのタイプに対して 1 つのシェーダーのみがメモリに存在します。

マテリアルとメッシュ タイプ

マテリアルは、マテリアルがどのようにレンダリングされるかの状態のセット (ブレンド モード、二面など) とマテリアルが様々なレンダリング パス (ベースカラー、ラフネス、法線など) とどのように相互作用するかを制御するマテリアル入力のセットによって定義されます。

頂点ファクトリ

マテリアルは様々なメッシュ タイプへの適用をサポートしなければなりません。これは頂点ファクトリによって実現します。FVertexFactoryType は、固有のメッシュタイプを表し、FVertexFactory インスタンスはインスタンス毎のデータを格納し、固有のメッシュタイプをサポートします。例えば、FGPUSkinVertexFactory はスキニングに必要なボーン マトリックス、およびGPU スキン頂点ファクトリ シェーダ コードが入力として必要とする様々な頂点バッファへの参照も格納します。頂点ファクトリ シェーダー コードは、暗黙的なインターフェイスであり、様々なパス シェーダーによってメッシュ タイプの違いを抽象化するために使用されます。頂点ファクトリは、主に頂点シェーダー コードから構成されますが、一部のピクセル シェーダー コードも含まれます。頂点ファクトリ シェーダー コードの重要なコンポーネントは以下の通りです。

関数

説明

FVertexFactoryInput

頂点シェーダーへの入力として頂点ファクトリが何を必要とするかを定義します。これらは、C++ 側の FVertexFactory の頂点の宣言と一致しなければなりません。

FVertexFactoryIntermediates

複数の頂点ファクトリ関数で使用されるキャッシュされた中間データを格納するために使用します。一般的な例は、TangentToLocal マトリックスであり、パックされない頂点入力から計算されなくてはなりません。

FVertexFactoryInterpolantsVSToPS

頂点シェーダーからピクセル シェーダーに渡される頂点ファクトリ データ。

VertexFactoryGetWorldPosition

ワールド空間頂点位置を得るために頂点シェーダーから呼び出されます。スタティック メッシュでは、これは単に LocalToWorld マトリックスを使用して頂点バッファからのローカル空間の位置をワールド空間に変換します。GPU スキン メッシュでは、この位置は最初にスキニングされ、次にワールド空間に変換されます。

VertexFactoryGetInterpolantsVSToPS

FVertexFactoryInput を FVertexFactoryInterpolants に変換します。これは、ピクセル シェーダーに渡される前にグラフィックス ハードウェアによって補間されます。

GetMaterialPixelParameters

これはピクセル シェーダーで呼び出され、頂点ファクトリ固有の補間式 (FVertexFactoryInterpolants) を、そのパスのピクセル シェーダーが使用する FMaterialPixelParameters 構造体に変換します。

マテリアル シェーダー

FMaterialShaderType を使用するシェーダーは、パス固有のシェーダーであり、いくつかのマテリアルの属性にアクセスする必要があります。従って、各マテリアルに対してコンパイルする必要がありますが、メッシュの属性にアクセスする必要はありません。ライト機能のパスのシェーダーは FMaterialShaderType の一例です。

FMeshMaterialShaderType を使用するシェーダーは、パス固有のシェーダーであり、マテリアルの属性およびメッシュのタイプに依存します。従って、各マテリアル / 頂点ファクトリの組み合わせに対してコンパイルされなければなりません。例えば、TBasePassVS / TBasePassPS はすべてのマテリアル入力を順送りのレンダリング パスで評価する必要があります。

マテリアルの必須シェーダー一式は FMaterialShaderMap にあります。以下のようなものです。

FMaterialShaderMap 
    FLightFunctionPixelShader - FMaterialShaderType
    FLocalVertexFactory - FVertexFactoryType
        TDepthOnlyPS - FMeshMaterialShaderType
        TDepthOnlyVS - FMeshMaterialShaderType
        TBasePassPS - FMeshMaterialShaderType
        TBasePassVS - FMeshMaterialShaderType
        Etc
    FGPUSkinVertexFactory - FVertexFactoryType
        Etc

頂点ファクトリは、マテリアルの用途に依存する ShouldCache 関数に基づきこのマトリックスに含まれます。例えば、bUsedWithSkeletalMesh が true だと GPU スキン頂点ファクトリが含まれます。FMeshMaterialShaderType は、マテリアルと頂点ファクトリの属性に依存するShouldCache 関数に基づきこのマトリックスに含まれます。これはシェーダーをキャッシングするための疎行列 (sparse matrix) アプローチであり、非常に速くシェーダーが多数になり、メモリを消費し、コンパイル時間が長くなります。実際に必要なシェーダーのリストを保存するのに比べて主な利点は、リストを生成する必要がなく、コンソールでのランタイム前に必要なシェーダーは常にコンパイル済みであるという点です。Unreal Engine ではシェーダーのメモリ問題をシェーダーの圧縮によって軽減し、コンパイル時間の問題はマルチコアのシェーダーのコンパイルで短縮しています。

マテリアル シェーダーを作成する

マテリアル シェーダーのタイプは DECLARE_SHADER_TYPE マクロで作成されます。

class FLightFunctionPixelShader : public FShader { DECLARE_SHADER_TYPE(FLightFunctionPixelShader,Material);

マテリアル シェーダーのタイプに対して必要なメタデータと関数を宣言します。マテリアル シェーダーのタイプは IMPLEMENT_MATERIAL_SHADER_TYPE: によってインスタンス化されます。

IMPLEMENT_MATERIAL_SHADER_TYPE(,FLightFunctionPixelShader,TEXT("LightFunctionPixelShader")

これは、マテリアルシェーダーのタイプのグローバル メタデータを生成します。これにより、ランタイムに任意のシェーダーのタイプを使用してすべてのシェーダーをイタレーションするなどが行えるようになります。

通常のマテリアルのピクセル シェーダーのタイプは、GetMaterialPixelParameters 頂点ファクトリ関数を呼び出すことで、最初に FMaterialPixelParameters 構造体を作成します。GetMaterialPixelParameters は、頂点ファクトリ固有の入力をどのパスでもアクセスするであろう WorldPosition、TangentNormal などのプロパティに変換します。次にマテリアル シェーダーは CalcMaterialParameters を呼び出します。これは、FMaterialPixelParameters のメンバーの残りを書き出し、その後、FMaterialPixelParameters が完全に初期化されます。次にマテリアル シェーダーは MaterialTemplate.usf (例えば、マテリアルのエミッシブ入力のための GetMaterialEmissive) を通してマテリアルの入力のいくつかにアクセスし、シェーディングを行い、そのパスの最終カラーを出力します。

特殊なエンジンのマテリアル

UMaterial には bUsedAsSpecialEngineMaterial と呼ばれる設定があり、どの頂点ファクトリ タイプでもマテリアルを使用できるようにします。つまり、すべての頂点ファクトリはマテリアルと共にコンパイルされますが、非常に大きなセットになります。bUsedAsSpecialEngineMaterial は以下に対して有用です。

  • ライティングのみ等のレンダリング ビューモードと合わせて使用するマテリアル。

  • コンパイル エラーがある場合にフォールバックとして使用するマテリアル (DefaultDecalMaterial、DefaultMaterial など)。

  • キャッシュしなければならないシェーダー数を削減するために他のマテリアルをレンダリングする場合に使用されるシェーダーのマテリアル。例えば、不透明なマテリアルのデプス専用のシェーダーは、DefaultMaterial と同じ深さ出力を生成します。そのため、代わりに DefaultMaterial のシェーダーが使用され、不透明なマテリアルはデプス専用のシェーダーのキャッシュをスキップします。

シェーダーのコンパイル

Unreal Engine では、ストリーミング システムを使用してシェーダーを非同期でコンパイルします。コンパイル要求は、キャッシュされたシェーダー マップを持たないマテリアルがロードしたときに待ち行列に入ります。利用可能な状態になるとコンパイル結果がエンジンをブロックすることなく適用されます。これはロード時間とコンパイルのスループットの観点では最適です。しかし、実際のプラットフォームのシェーダーのコンパイルとそれを要求したマテリアルとの間には多くのレイヤーがあることを意味します。

実際のコンパイル作業は Shader Compile Workers と呼ばれるヘルパー プロセスで行われます。プラットフォームのシェーダー コンパイル関数 (D3DCompile) には多くの場合、単一プロセス内でのマルチコアのスケーリングを妨げるクリティカルなセクションを内部に含むからです。

シェーダー コンパイラのデバッグ

コンパイルをどのように行うかを制御する設定がいくつかあります。これにより、シェーダー コンパイラのデバッグを単純化できます。これは、BaseEngine.ini の [DevOptions.Shaders] セクションにあります。

設定

説明

bAllowCompilingThroughWorkers

SCW を起動してコンパイラの DLL を呼び出すか、Unreal Engine がコンパイラの DLL を直接呼び出すかを設定します。無効な場合、コンパイルはシングル コアになります。

bAllowAsynchronousShaderCompiling

Unreal Engine 内の別のスレッドでコンパイルを行うか否かを設定します。

Unreal Engine から直接シェーダー コンパイラの DLL にステップインしたい場合 (例、CompileD3D11Shader)、これらを両方とも false に設定します。しかしコンパイルには長時間かかります。したがって、他のすべてのシェーダーがキャッシュ済みであるようにしてください。

コンパイラ エラーでリトライする

r.ShaderDevelopmentMode が有効な状態では、シェーダー コンパイル エラーでリトライする機会があります。これは、コンパイルが失敗すると致命的なエラーになるグローバル シェーダーの場合に特に重要です。

デバッグでは、デバッガーがアタッチされた状態でブレークポイントになり、 Visual Studio の出力ウィンドウでコンパイル エラーになります。次にエラー ログを ダブルクリック して問題のある行に直接移動します。

CompilerErrorDebug.png

上記が該当しない場合は、Yes/No ダイアログになります。

CompileError.png

シェーダーのキャッシシングとクッキング

シェーダーがコンパイルされると、派生データのキャッシュ (DDC) に入ります。DDC には、そのキーにシェーダー ソース ファイルを含むコンパイルへのすべての入力のハッシュが含まれます。つまり、シェーダー ソース ファイルへの変更はエンジンを再起動するか、'recompileshaders changed' を行う毎に自動的にピックアップされます。

FShader Serialize 関数を修正する場合、後方互換性を処理する必要はなく、そのシェーダーに含まれるシェーダー ファイルにスペースを追加するだけです。

アセットをクックする場合、マテリアル シェーダーはマテリアルのパッケージにインライン化され、グローバル シェーダーはグローバル シェーダー ファイルに別個に格納されます。これにより、エンジンの起動時に速くロードできるようになります。

デバッグ

シェーダーのデバッグの主な方法は、シェーダーに中間ファイルを出力させるようにシェーダーを修正し、それを適切な VisualizeTexture コマンドでビジュアル化します。これにより高速イタレーションが実現します。エンジンを再起動することなくオンザフライでコンパイルできるからです。例えば、以下のようにして WorldPosition が正しいことを確認できます。

OutColor = frac(WorldPosition / 1000);

次にスケールが正しいことと、結果が視点依存になっていることを確認します。しかし、この方法ではデータ構造をビルドするような複雑なシェーダーはうまくスケーリングしません。

デバッグ情報をダンプする

r.DumpShaderDebugInfo=1 を使用してコンパイルするすべてのシェーダーに対してすべてのファイルをディスクに保存することができます。r.ShaderDevelopmentMode のような ConsoleVariables.ini にこれを設定する場合に役立ちます。以下を含むファイルは、GameName/Saved/ShaderDebugInfo に格納されます。

  • ソース ファイルとインクルード ファイル

  • プリプロセスされたシェーダーのバージョン

  • 使用したコンパイラと同等のコマンドライン オプションを持つプリプロセス バージョンをコンパイルするためのバッチファイル

    この設定をオンにしたままにすると、HD が多くの小さなファイルとフォルダでいっぱいになることがあります。

    イタレーションのベスト プラクティス

グローバル シェーダーで作業をしている場合、recompileshaders changed または Ctrl+Shift+. は最速でイタレーションを行います。シェーダーのコンパイルに長時間かかる場合は、CFLAG_StandardOptimization を、シェーダーのModifyCompilationEnvironment のコンパイル フラグとして指定することを検討します。

マテリアル シェーダーで作業している場合、BasePassPixelShader.usf のように単一のマテリアルでのイタレーションがはるかに速くなります。マテリアル エディタで [Apply (適用)] ボタンをクリックするたびに、ディスクからシェーダー ファイルを再読み込みし、そのマテリアルだけを再コンパイルします。

クロス コンパイラ

HLSL クロス コンパイラ を使用して OpenGL プラットフォーム向けに HLSL をGLSL に自動的に変換します。これにより、シェーダーをすべてのプラットフォームに対して一度だけオーサリングできます。オフラインのシェーダーのコンパイル時に実行し、OpenGL ドライバでは行われないことが多いコードに対する様々な最適化を行います。

AsyncCompute

AsyncCompute は、特定の GPU で機能する API で利用可能なハードウェア機能です。 GPU のハードウェア ユニットをより効率的に活用できるようにインターリーブします。

Unreal Engine のドキュメントを改善するために協力をお願いします!どのような改善を望んでいるかご意見をお聞かせください。
調査に参加する
キャンセル