Schnittstellen und Klassen

Dynamische Shaderverknüpfung verwendet schnittstellen und Klassen mit hoher Shadersprache (HLSL) und Klassen, die syntactisch mit ihren C++-Gegenstücken vergleichbar sind. Dadurch können Shader auf abstrakte Schnittstelleninstanzen zur Kompilierungszeit verweisen und die Auflösung dieser Instanzen auf konkrete Klassen für die Anwendung zur Laufzeit verlassen.

In den folgenden Abschnitten erfahren Sie, wie Sie einen Shader einrichten, um Schnittstellen und Klassen zu verwenden und wie Schnittstelleninstanzen im Anwendungscode initialisiert werden.

Deklarieren von Schnittstellen

Eine Schnittstelle funktioniert auf ähnliche Weise wie eine abstrakte Basisklasse in C++. Eine Schnittstelle wird in einem Shader mithilfe des Schnittstellenschlüsselworts deklariert und enthält nur Methodendeklarationen. Die in einer Schnittstelle deklarierten Methoden sind alle virtuellen Methoden in allen Klassen, die aus der Schnittstelle abgeleitet wurden. Abgeleitete Klassen müssen alle Methoden implementieren, die in einer Schnittstelle deklariert sind. Beachten Sie, dass Schnittstellen die einzige Möglichkeit sind, virtuelle Methoden zu deklarieren, es gibt kein virtuelles Schlüsselwort wie in C++, und Klassen deklarieren virtuelle Methoden.

Der folgende Beispiel-Shadercode deklariert zwei Schnittstellen.

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

};
      

Deklarieren von Klassen

Eine Klasse verhält sich ähnlich wie Klassen in C++. Eine Klasse wird mit dem Klassenschlüsselwort deklariert und kann Membervariablen und Methoden enthalten. Eine Klasse kann von null oder einer Klasse und null oder mehr Schnittstellen erben. Klassen müssen Implementierungen für alle Schnittstellen in ihrer Vererbungskette implementieren oder erben, oder die Klasse kann nicht instanziiert werden.

Im folgenden Beispiel-Shadercode wird veranschaulicht, dass eine Klasse aus einer Schnittstelle und von einer anderen Klasse abgeleitet wird.

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

Schnittstelleninstanzdeklarationen in einem Shader

Eine Schnittstelleninstanz fungiert als Platzhalter für Klasseninstanzen, die eine Implementierung der Methoden der Schnittstelle bereitstellen. Mithilfe einer Instanz einer Schnittstelle kann Shadercode eine Methode aufrufen, ohne zu wissen, welche Implementierung dieser Methode aufgerufen wird. Shadercode deklariert eine oder mehrere Instanzen für jede Benutzeroberfläche, die sie definiert. Diese Instanzen werden in Shadercode auf ähnliche Weise wie C++-Basisklassenzeiger verwendet.

Der folgende Beispiel-Shadercode veranschaulicht das Deklarieren mehrerer Schnittstelleninstanzen und die Verwendung in Shadercode.

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

Klasseninstanzdeklarationen in einem Shader

Jede Klasse, die anstelle einer Schnittstelleninstanz verwendet wird, muss entweder als Variable in einem Konstantenpuffer deklariert oder von der Anwendung zur Laufzeit mithilfe der ID3D11ClassLinkage::CreateClassInstance-Methode erstellt werden. Schnittstelleninstanzen werden auf Klasseninstanzen im Anwendungscode verwiesen. Klasseninstanzen können in Shadercode wie jeder anderen Variable referenziert werden, aber eine klasse, die von einer Schnittstelle abgeleitet wird, wird normalerweise nur mit einer Schnittstelleninstanz verwendet und wird nicht direkt von Shadercode verwiesen.

Der folgende Beispiel-Shadercode veranschaulicht das Deklarieren mehrerer Klasseninstanzen.

cbuffer cbPerFrame : register( b0 )
{
   cAmbientLight     g_ambientLight;
   cHemiAmbientLight g_hemiAmbientLight;
   cDirectionalLight g_directionalLight;
   cEnvironmentLight g_environmentLight;
   float4            g_vEyeDir;   
};        
      

Initialisieren von Schnittstelleninstanzen in einer Anwendung

Schnittstelleninstanzen werden im Anwendungscode initialisiert, indem ein dynamisches Verknüpfungsarray mit Schnittstellenzuweisungen an eine der METHODEN ID3D11DeviceContext SetShader übergeben wird.

Verwenden Sie die folgenden Schritte, um ein dynamisches Verknüpfungsarray zu erstellen

  1. Erstellen Sie ein Klassenverknüpfungsobjekt mithilfe von CreateClassLinkage.

    ID3D11ClassLinkage* g_pPSClassLinkage = NULL;            
    pd3dDevice->CreateClassLinkage( &g_pPSClassLinkage );
    
    
  2. Erstellen Sie den Shader, der dynamische Klassenverknüpfung verwendet, und übergeben Sie das Klassenverknüpfungsobjekt als Parameter an die Erstellungsfunktion des Shaders.

    pd3dDevice->CreatePixelShader( pPixelShaderBuffer->GetBufferPointer(),
        pPixelShaderBuffer->GetBufferSize(), g_pPSClassLinkage, &g_pPixelShader ) );            
    
    
  3. Erstellen Sie ein ID3D11ShaderReflection-Objekt mithilfe der D3DReflect-Funktion .

    ID3D11ShaderReflection* pReflector = NULL; 
    D3DReflect( pPixelShaderBuffer->GetBufferPointer(),                  
        pPixelShaderBuffer->GetBufferSize(), 
        IID_ID3D11ShaderReflection, (void**) &pReflector) );            
    
    
  4. Verwenden Sie das Shader-Spiegelungsobjekt, um die Anzahl der Schnittstelleninstanzen im Shader mithilfe der ID3D11ShaderReflection::GetNumInterfaceSlots-Methode abzurufen.

    g_iNumPSInterfaces = pReflector->GetNumInterfaceSlots();             
    
    
  5. Erstellen Sie ein Array, das groß genug ist, um die Anzahl der Schnittstelleninstanzen im Shader zu halten.

    ID3D11ClassInstance** g_dynamicLinkageArray = NULL;            
    g_dynamicLinkageArray = 
        (ID3D11ClassInstance**) malloc( sizeof(ID3D11ClassInstance*) * g_iNumPSInterfaces );            
    
    
  6. Bestimmen Sie den Index im Array, der jeder Schnittstelleninstanz mit ID3D11ShaderReflection::GetVariableByName und ID3D11ShaderReflectionVariable::GetInterfaceSlot entspricht.

    ID3D11ShaderReflectionVariable* pAmbientLightingVar = 
        pReflector->GetVariableByName("g_abstractAmbientLighting");
        g_iAmbientLightingOffset = pAmbientLightingVar->GetInterfaceSlot(0);            
    
    
  7. Rufen Sie eine Klasseninstanz für jedes Klassenobjekt ab, das von einer Schnittstelle im Shader mit ID3D11ClassLinkage::GetClassInstance abgeleitet wurde.

    g_pPSClassLinkage->GetClassInstance( "g_hemiAmbientLight", 0, 
        &g_pHemiAmbientLightClass );            
    
    
  8. Legen Sie Schnittstelleninstanzen auf Klasseninstanzen fest, indem Sie den entsprechenden Eintrag im dynamischen Verknüpfungsarray festlegen.

    g_dynamicLinkageArray[g_iAmbientLightingOffset] = g_pHemiAmbientLightClass;            
    
    
  9. Übergeben Sie das dynamische Verknüpfungsarray als Parameter an einen SetShader-Aufruf.

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

Dynamische Verknüpfung