インターフェイスとクラス
動的シェーダー リンクでは、上位レベル シェーダー言語 (HLSL) のインターフェイスとクラスを使用します。これらの構文は C++ の対応するインターフェイスおよびクラスと類似しています。この類似性により、シェーダーではコンパイル時に抽象インターフェイス インスタンスを参照し、具象クラスについてはアプリケーションで実行時に解決できます。
ここでは、インターフェイスおよびクラスを使用するようにシェーダーを設定する方法と、アプリケーション コードでインターフェイス インスタンスを初期化する方法について説明します。
- インターフェイスの宣言
- クラスの宣言
- シェーダーでのインターフェイス インスタンスの宣言
- シェーダーでのクラス インスタンスの宣言
- アプリケーションでのインターフェイス インスタンスの初期化
インターフェイスの宣言
インターフェイスは C++ の抽象基本クラスと同じように機能します。インターフェイスは、インターフェイス キーワードを使用してシェーダーで宣言します。ここではメソッド宣言のみを指定します。インターフェイスで宣言したメソッドはすべて、そのインターフェイスのあらゆる派生クラスの仮想メソッドになります。派生クラスには、インターフェイスで宣言したすべてのメソッドを実装する必要があります。仮想メソッドはインターフェイスでのみ宣言できます。C++ のような仮想キーワードは用意されていないので、クラスで仮想メソッドを宣言することはできません。
次のシェーダー コードの例では、2 つのインターフェイスを宣言しています。
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(); };
クラスの宣言
クラスは C++ のクラスと同じように動作します。クラスは、クラス キーワードを使用して宣言します。クラスにはメンバー変数とメソッドを設定できます。クラスは、0 個または 1 つのクラスおよび 0 個以上のインターフェイスを継承できます。クラスは、すべてのインターフェイスを実装するか、その継承チェーンですべてのインターフェイスの実装を継承する必要があります。このようにしないと、そのクラスをインスタンス化できません。
次のシェーダー コードの例は、インターフェイスからの派生クラスの作成と、別のクラスからの派生クラスの作成を示しています。
class cAmbientLight : iBaseLight { float3 m_vLightColor; bool m_bEnable; float3 IlluminateAmbient(float3 vNormal); float3 IlluminateDiffuse(float3 vNormal); float3 IlluminateSpecular(float3 vNormal, int specularPower ); }; class cHemiAmbientLight : cAmbientLight { float4 m_vGroundColor; float4 m_vDirUp; float3 IlluminateAmbient(float3 vNormal); };
シェーダーでのインターフェイス インスタンスの宣言
インターフェイス インスタンスは、インターフェイスのメソッドの実装を提供するクラス インスタンスのプレース ホルダーとして機能します。インターフェイスのインスタンスを使用することで、シェーダー コードでは、メソッドを呼び出すことでその実装のどれが呼び出されるかを認識していなくても、そのメソッドを呼び出すことができます。シェーダー コードでは、定義するインターフェイスごとに 1 つ以上のインスタンスを宣言します。これらのインスタンスは、C++ の基本クラス ポインターと同じようにシェーダー コードで使用します。
次のシェーダー コードの例では、いくつかのインターフェイス インスタンスを宣言し、シェーダー コードでそれらを使用しています。
// Declare interface instances iBaseLight g_abstractAmbientLighting; iBaseLight g_abstractDirectLighting; iBaseMaterial g_abstractMaterial; struct PS_INPUT { float4 vPosition : SV_POSITION; float3 vNormal : NORMAL; float2 vTexcoord : TEXCOORD0; }; float4 PSMain( PS_INPUT Input ) : SV_TARGET { float3 Ambient = (float3)0.0f; Ambient = g_abstractMaterial.GetAmbientColor( Input.vTexcoord ) * g_abstractAmbientLighting.IlluminateAmbient( Input.vNormal ); float3 Diffuse = (float3)0.0f; Diffuse += g_abstractMaterial.GetDiffuseColor( Input.vTexcoord ) * g_abstractDirectLighting.IlluminateDiffuse( Input.vNormal ); float3 Specular = (float3)0.0f; Specular += g_abstractDirectLighting.IlluminateSpecular( Input.vNormal, g_abstractMaterial.GetSpecularPower() ); float3 Lighting = saturate( Ambient + Diffuse + Specular ); return float4(Lighting,1.0f); }
シェーダーでのクラス インスタンスの宣言
インターフェイス インスタンスの代わりに使用する各クラスは、定数バッファーで変数として宣言するか、ID3D11ClassLinkage::CreateClassInstance メソッドを使用してアプリケーションで実行時に作成する必要があります。アプリケーション コードでは、インターフェイス インスタンスでクラス インスタンスをポイントします。クラス インスタンスは、シェーダー コードで他の変数と同じように参照できますが、インターフェイスの派生クラスは、インターフェイス インスタンスでのみ使用することが普通で、シェーダー コードで直接参照することはありません。
次のシェーダー コードの例は、いくつかのクラス インスタンスの宣言を示しています。
cbuffer cbPerFrame : register( b0 ) { cAmbientLight g_ambientLight; cHemiAmbientLight g_hemiAmbientLight; cDirectionalLight g_directionalLight; cEnvironmentLight g_environmentLight; float4 g_vEyeDir; };
アプリケーションでのインターフェイス インスタンスの初期化
アプリケーション コードでインターフェイス インスタンスを初期化するには、ID3D11DeviceContext のいずれかの SetShader メソッドに対するインターフェイスの割り当てを格納した動的リンク配列を渡します。
動的リンク配列を作成する手順は次のとおりです。
CreatClassLinkage を使用して、クラス リンク オブジェクトを作成します。
ID3D11ClassLinkage* g_pPSClassLinkage = NULL; pd3dDevice->CreateClassLinkage( &g_pPSClassLinkage );
動的クラス リンクを使用するシェーダーを作成し、そのクラス リンク オブジェクトをパラメーターとしてシェーダーの create 関数に渡します。
pd3dDevice->CreatePixelShader( pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(), g_pPSClassLinkage, &g_pPixelShader ) );
D3DReflect 関数を使用して、ID3D11ShaderReflection オブジェクトを作成します。
ID3D11ShaderReflection* pReflector = NULL; D3DReflect( pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(), IID_ID3D11ShaderReflection, (void**) &pReflector) );
シェーダー リフレクション オブジェクトと ID3D11ShaderReflection::GetNumInterfaceSlots メソッドを使用して、シェーダーにあるインターフェイス インスタンスの数を取得します。
g_iNumPSInterfaces = pReflector->GetNumInterfaceSlots();
シェーダーにあるインターフェイス インタンスを保持するうえで十分な大きさの配列を作成します。
ID3D11ClassInstance** g_dynamicLinkageArray = NULL; g_dynamicLinkageArray = (ID3D11ClassInstance**) malloc( sizeof(ID3D11ClassInstance*) * g_iNumPSInterfaces );
ID3D11ShaderReflection::GetVariableByName と ID3D11ShaderReflectionVariable::GetInterfaceSlot を使用して、この配列の中で各インターフェイス インスタンスに対応するインデックスを判別します。
ID3D11ShaderReflectionVariable* pAmbientLightingVar = pReflector->GetVariableByName("g_abstractAmbientLighting"); g_iAmbientLightingOffset = pAmbientLightingVar->GetInterfaceSlot(0);
ID3D11ClassLinkage::GetClassInstance を使用して、シェーダーにあるインターフェイスから派生した各クラス オブジェクトのクラス インスタンスを取得します。
g_pPSClassLinkage->GetClassInstance( "g_hemiAmbientLight", 0, &g_pHemiAmbientLightClass );
動的リンク配列の中の対応するエントリを設定することによって、クラス インスタンスにインターフェイス インスタンスを設定します。
g_dynamicLinkageArray[g_iAmbientLightingOffset] = g_pHemiAmbientLightClass;
動的リンク配列をパラメーターとして SetShader 呼び出しに渡します。
pd3dImmediateContext->PSSetShader( g_pPixelShader, g_dynamicLinkageArray, g_iNumPSInterfaces );