DynamicShaderLinkage11 サンプル
Direct3D 11 サンプルでは、シェーダー モデル 5 のシェーダー インターフェイスと Direct3D 11 のサポートを使用して、実行時にシェーダー インターフェイス メソッドをリンクする方法を示します。
パス
ソース | SDK ルート\Samples\C++\Direct3D11\DynamicShaderLinkage11 |
実行可能ファイル | SDK ルート\Samples\C++\Direct3D11\Bin\x86 または x64\DynamicShaderLinkage11.exe |
シェーダー置換エクスプロージョンの対処方法
システムのレンダリングでは、シェーダーを管理するうえでの多様な複雑さに対処する一方で、できる限りシェーダー コードを最適化することが求められます。これは、利用可能な幅広いハードウェア構成において、レンダリングされたシーン内のさまざまなマテリアルをサポートする必要性を考えたとき、より大きな課題となります。これらの課題に対処するために、多くのシェーダー開発者は一般的な 2 つのアプローチのいずれかを実施してきました。1 つは、高機能 "Uber シェーダー" の作成ですが、このアプローチでは柔軟性と引き換えにパフォーマンスが多少犠牲になります。もう 1 つは、必要とされる各ジオメトリ ストリーム、マテリアル タイプまたはライト タイプの組み合わせにおける個々のシェーダーの作成です。
Uber シェーダーでは、この組み合わせの問題に対して、異なるプリプロセッサ定義を使用することで同じシェーダーを再コンパイルして対処します。一方、後者のメソッドでは、開発者は総力戦で同じ目的を達成する必要があります。このシェーダー置換エクスプロージョンは、何千もの異なるシェーダー置換をゲームおよびアセット パイプライン内で管理する必要に迫られる開発者にとって、たびたび問題となってきました。
Direct3D 11 およびシェーダー モデル 5 では、開発者がこれらの開発時の問題に対処しやすいようにオブジェクト指向言語構造を採用することで、シェーダー リンクのランタイム サポートを提供しています。このサンプルでは、シェーダー インターフェイスを使用して、生成されたシェーダー置換の数を管理しやすくしています。
シェーダー モデル 5 のインターフェイスおよびクラス
ピクセル シェーダーでは、まず異なるライト タイプやマテリアル タイプのためのベース インターフェイスを定義します。
//-------------------------------------------------------------------------------------- // Interfaces //-------------------------------------------------------------------------------------- interface iBaseLight { float3 IlluminateAmbient(float3 vNormal); float3 IlluminateDiffuse(float3 vNormal); float3 IlluminateSpecular(float3 vNormal, int specularPower ); }; // ... interface iBaseMaterial { float3 GetAmbientColor(float2 vTexcoord); float3 GetDiffuseColor(float2 vTexcoord); int GetSpecularPower(); }; // ...
次に、それらのインターフェイスに基づいて特化されたクラスを、必要に応じて機能を追加しながらビルドします。
//-------------------------------------------------------------------------------------- // Classes //-------------------------------------------------------------------------------------- class cAmbientLight : iBaseLight { float3 m_vLightColor; bool m_bEnable; float3 IlluminateAmbient(float3 vNormal); float3 IlluminateDiffuse(float3 vNormal); float3 IlluminateSpecular(float3 vNormal, int specularPower ); }; // ... class cBaseMaterial : iBaseMaterial { float3 m_vColor; int m_iSpecPower; float3 GetAmbientColor(float2 vTexcoord); float3 GetDiffuseColor(float2 vTexcoord); int GetSpecularPower(); }; // ....
抽象インスタンスおよびシェーダーの Main 関数
ピクセル シェーダーの main 関数では、計算にインターフェイスの抽象インスタンスを使用します。これらのインターフェイス インスタンスは、シェーダーのバインド時にアプリケーション コードによって具象化されます。
/-------------------------------------------------------------------------------------- // Abstract Interface Instances for dyamic linkage / permutation //-------------------------------------------------------------------------------------- iBaseLight g_abstractAmbientLighting; iBaseLight g_abstractDirectLighting; iBaseMaterial g_abstractMaterial; //-------------------------------------------------------------------------------------- // Pixel Shader //-------------------------------------------------------------------------------------- float4 PSMain( PS_INPUT Input ) : SV_TARGET { // Compute the Ambient term float3 Ambient = (float3)0.0f; Ambient = g_abstractMaterial.GetAmbientColor( Input.vTexcoord ) * g_abstractAmbientLighting.IlluminateAmbient( Input.vNormal ); // Accumulate the Diffuse contribution float3 Diffuse = (float3)0.0f; Diffuse += g_abstractMaterial.GetDiffuseColor( Input.vTexcoord ) * g_abstractDirectLighting.IlluminateDiffuse( Input.vNormal ); // Compute the Specular contribution float3 Specular = (float3)0.0f; Specular += g_abstractDirectLighting.IlluminateSpecular( Input.vNormal, g_abstractMaterial.GetSpecularPower() ); // Accumulate the lighting with saturation float3 Lighting = saturate( Ambient + Diffuse + Specular ); return float4(Lighting,1.0f); }
ここでは、汎用/抽象ライト インスタンス全体のピクセルのライティングを単に蓄積します。
アプリケーション コードおよびシェーダー インターフェイス
シェーダーがコンパイルされてロードされたら、サンプル アプリケーション コードはシェーダー反射レイヤーを利用して、コンパイル済みシェーダー内の具象化および抽象化のシェーダー クラス インスタンスのオフセットを取得します。次に、これらのオフセットは、ユーザーが選択したオプションに基づいてシェーダー コード パスを動的に構成するために使用されます。Direct3D 11 の実行時にリンケージが直接生成され、PSSetShader の呼び出し時に指定されます。
シェーダー インターフェイスの取得
シェーダーのコンパイルが完了したら、次のとおり置換可能なシェーダー オブジェクトの抽象インスタンスのオフセットを取得する必要があります。
// use shader reflection to get data locations for the interface arrayID3D11ShaderReflection* pReflector = NULL;V_RETURN( D3DReflect( pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(),IID_ID3D11ShaderReflection, (void**) &pReflector) );g_iNumPSInterfaces = pReflector->GetNumInterfaceSlots();g_dynamicLinkageArray = (ID3D11ClassInstance**) malloc( sizeof(ID3D11ClassInstance*) * g_iNumPSInterfaces );if (g_dynamicLinkageArray == NULL)return E_FAIL;ID3D11ShaderReflectionVariable* pAmbientLightingVar = pReflector->GetVariableByName("g_abstractAmbientLighting");g_iAmbientLightingOffset = pAmbientLightingVar->GetInterfaceSlot(0);ID3D11ShaderReflectionVariable* pDirectLightingVar = pReflector->GetVariableByName("g_abstractDirectLighting");g_iDirectLightingOffset = pDirectLightingVar->GetInterfaceSlot(0);ID3D11ShaderReflectionVariable* pMaterialVar = pReflector->GetVariableByName("g_abstractMaterial");g_iMaterialOffset = pMaterialVar->GetInterfaceSlot(0); // ...
次に、シェーダーに存在するマテリアル オブジェクトの考えられるすべての置換を列挙します。
// Material Dynamic Permutationenum E_MATERIAL_TYPES{ MATERIAL_PLASTIC, MATERIAL_PLASTIC_TEXTURED, MATERIAL_PLASTIC_LIGHTING_ONLY, MATERIAL_ROUGH, MATERIAL_ROUGH_TEXTURED, MATERIAL_ROUGH_LIGHTING_ONLY, MATERIAL_TYPE_COUNT};char* g_pMaterialClassNames[ MATERIAL_TYPE_COUNT ] ={ "g_plasticMaterial", // cPlasticMaterial "g_plasticTexturedMaterial", // cPlasticTexturedMaterial "g_plasticLightingOnlyMaterial", // cPlasticLightingOnlyMaterial "g_roughMaterial", // cRoughMaterial "g_roughTexturedMaterial", // cRoughTexturedMaterial "g_roughLightingOnlyMaterial" // cRoughLightingOnlyMaterial};E_MATERIAL_TYPES g_iMaterial = MATERIAL_PLASTIC_TEXTURED;// ...// Acquire the material Class Instances for all possible material settingsfor( UINT i=0; i < MATERIAL_TYPE_COUNT; i++){ g_pPSClassLinkage->GetClassInstance( g_pMaterialClassNames[i], 0, &g_pMaterialClasses[i] );}
ランタイムへのリンケージの指定
ユーザーは、ライティングやマテリアルの具体的な構成をサンプルのユーザー インターフェイスから指定します。アプリケーション コードでは、シェーダー バインド時において、ユーザーが指定した設定に基づいて、シェーダー コード バインドが次のように選択的に設定されます。
// Setup the Shader Linkage based on the user settings for Lighting // Ambient Lighting First - Constant or Hemi? if ( g_bHemiAmbientLighting ) g_dynamicLinkageArray[g_iAmbientLightingOffset] = g_pHemiAmbientLightClass; else g_dynamicLinkageArray[g_iAmbientLightingOffset] = g_pAmbientLightClass; // Direct Light - None or Directional TODO: or Spot or Environment? if (g_bDirectLighting) g_dynamicLinkageArray[g_iDirectLightingOffset] = g_pDirectionalLightClass; else g_dynamicLinkageArray[g_iDirectLightingOffset] = g_pAmbientLightClass; // Use the selected material class instance if (g_bLightingOnly) { switch( g_iMaterial ) { case MATERIAL_PLASTIC: case MATERIAL_PLASTIC_TEXTURED: g_dynamicLinkageArray[g_iMaterialOffset] = g_pMaterialClasses[ MATERIAL_PLASTIC_LIGHTING_ONLY ]; break; case MATERIAL_ROUGH: case MATERIAL_ROUGH_TEXTURED: g_dynamicLinkageArray[g_iMaterialOffset] = g_pMaterialClasses[ MATERIAL_ROUGH_LIGHTING_ONLY ]; break; } } else g_dynamicLinkageArray[g_iMaterialOffset] = g_pMaterialClasses[ g_iMaterial ] ;
Direct3D 11 ランタイムが、選択されたそれぞれのメソッドにソース レベルで効率的にリンクされます。これにより、シェーダー コードを可能な限りインライン化して最適化することができ、GPU での実行に最適なシェーダーが得られます。