共用方式為


介面和類別

動態著色器連結會使用高階著色器語言 (HLSL) 介面和語法類似其C++對應專案。 這可讓著色器在編譯階段參考抽象介面實例,並在運行時間將這些實例的解析保留為應用程式的具體類別。

下列各節詳細說明如何設定著色器以使用介面和類別,以及如何在應用程式程式代碼中初始化介面實例。

宣告介面

介面函式的方式與C++中的抽象基類類似。 介面是使用 interface 關鍵詞在著色器中宣告,且只包含方法宣告。 在介面中宣告的方法全都是衍生自 介面的任何類別中的虛擬方法。 衍生類別必須實作介面中宣告的所有方法。 請注意,介面是宣告虛擬方法的唯一方法,C++中沒有虛擬關鍵詞,而類別則表示宣告虛擬方法。

下列範例著色器程式代碼會宣告兩個介面。

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++中的類別類似。 類別是以 class 關鍵詞宣告,而且可以包含成員變數和方法。 類別可以繼承自零或一個類別,以及零個或多個介面。 類別必須為其繼承鏈結中的所有介面實作或繼承實作,否則無法具現化類別。

下列範例著色器程式代碼說明從介面和另一個類別衍生類別。

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);
};        
      

著色器中的介面實例宣告

介面實例可作為提供介面方法實作之類別實例的佔位元。 使用介面的實例可讓著色器程式代碼呼叫方法,而不知道該方法的實作將叫用哪一個。 著色器程式代碼會針對它定義的每個介面宣告一或多個實例。 這些實例會以類似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 方法。

若要建立動態連結陣列,請使用下列步驟

  1. 使用 CreateClassLinkage建立類別連結物件。

    ID3D11ClassLinkage* g_pPSClassLinkage = NULL;            
    pd3dDevice->CreateClassLinkage( &g_pPSClassLinkage );
    
    
  2. 建立將使用動態類別連結的著色器,將類別連結對象當做參數傳遞至著色器的 create 函式。

    pd3dDevice->CreatePixelShader( pPixelShaderBuffer->GetBufferPointer(),
        pPixelShaderBuffer->GetBufferSize(), g_pPSClassLinkage, &g_pPixelShader ) );            
    
    
  3. 使用 D3DReflect 函式建立 ID3D11ShaderReflection 物件。

    ID3D11ShaderReflection* pReflector = NULL; 
    D3DReflect( pPixelShaderBuffer->GetBufferPointer(),                  
        pPixelShaderBuffer->GetBufferSize(), 
        IID_ID3D11ShaderReflection, (void**) &pReflector) );            
    
    
  4. 使用著色器反映物件,使用 ID3D11ShaderReflection::GetNumInterfaceSlots 方法來取得著色器中的介面實例數目。

    g_iNumPSInterfaces = pReflector->GetNumInterfaceSlots();             
    
    
  5. 建立夠大的陣列,以保存著色器中的介面實例數目。

    ID3D11ClassInstance** g_dynamicLinkageArray = NULL;            
    g_dynamicLinkageArray = 
        (ID3D11ClassInstance**) malloc( sizeof(ID3D11ClassInstance*) * g_iNumPSInterfaces );            
    
    
  6. 使用 ID3D11ShaderReflection::GetVariableByNameID3D11ShaderReflectionVariable::GetInterfaceSlot,判斷陣列中對應至每個介面實例的索引。

    ID3D11ShaderReflectionVariable* pAmbientLightingVar = 
        pReflector->GetVariableByName("g_abstractAmbientLighting");
        g_iAmbientLightingOffset = pAmbientLightingVar->GetInterfaceSlot(0);            
    
    
  7. 使用 ID3D11ClassLinkage::GetClassInstance,為著色器中衍生自介面的每個類別物件取得類別實例。

    g_pPSClassLinkage->GetClassInstance( "g_hemiAmbientLight", 0, 
        &g_pHemiAmbientLightClass );            
    
    
  8. 藉由在動態連結陣列中設定對應的專案,將介面實例設定為類別實例。

    g_dynamicLinkageArray[g_iAmbientLightingOffset] = g_pHemiAmbientLightClass;            
    
    
  9. 將動態連結陣組當做參數傳遞至 SetShader 呼叫。

    pd3dImmediateContext->PSSetShader( g_pPixelShader, g_dynamicLinkageArray, g_iNumPSInterfaces );            
    
    

動態連結