共用方式為


介面和類別

動態著色器連結會使用高階著色器語言 (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 );            
    
    

動態連結