インターフェイスとクラス
動的シェーダー リンケージでは、高度なシェーダー言語 (HLSL) インターフェイスと、C++ に対応するインターフェイスと構文的に似たクラスを使用します。 これにより、シェーダーはコンパイル時に抽象インターフェイス インスタンスを参照し、実行時にアプリケーションの具象クラスにそれらのインスタンスの解決を残すことができます。
次のセクションでは、インターフェイスとクラスを使用するようにシェーダーを設定する方法と、アプリケーション コードでインターフェイス インスタンスを初期化する方法について詳しく説明します。
インターフェイスの宣言
インターフェイス関数は、C++ の抽象基本クラスと同様の方法で機能します。 インターフェイスは、インターフェイス キーワード (keyword)を使用してシェーダーで宣言され、メソッド宣言のみが含まれます。 インターフェイスで宣言されたメソッドはすべて、 インターフェイスから派生した任意のクラスの仮想メソッドになります。 派生クラスは、インターフェイスで宣言されているすべてのメソッドを実装する必要があります。 インターフェイスは仮想メソッドを宣言する唯一の方法であり、C++ のように仮想キーワード (keyword)はなく、クラスは仮想メソッドを宣言しません。
次のシェーダー コードの例では、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++ のクラスと同様の方法で動作します。 クラスはクラス キーワード (keyword)で宣言され、メンバー変数とメソッドを含めることができます。 クラスは、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 メソッドのいずれかにインターフェイス割り当てを含む動的リンケージ配列を渡すことによって、アプリケーション コードで初期化されます。
動的リンケージ配列を作成するには、次の手順に従います。
CreateClassLinkage を使用してクラス リンケージ オブジェクトを作成します。
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 );