Interfaces y clases

La vinculación dinámica del sombreador usa interfaces y clases de lenguaje de sombreador de alto nivel (HLSL) que son sintácticamente similares a sus homólogos de C++. Esto permite a los sombreadores hacer referencia a instancias de interfaz abstractas en tiempo de compilación y dejar la resolución de esas instancias en clases concretas para la aplicación en tiempo de ejecución.

En las secciones siguientes se detalla cómo configurar un sombreador para usar interfaces y clases y cómo inicializar instancias de interfaz en el código de aplicación.

Declaración de interfaces

Una interfaz funciona de forma similar a una clase base abstracta en C++. Una interfaz se declara en un sombreador mediante la palabra clave interface y solo contiene declaraciones de método. Todos los métodos declarados en una interfaz serán métodos virtuales en cualquier clase derivada de la interfaz . Las clases derivadas deben implementar todos los métodos declarados en una interfaz. Tenga en cuenta que las interfaces son la única manera de declarar métodos virtuales, no hay ninguna palabra clave virtual como en C++y las clases no declaran métodos virtuales.

El código del sombreador de ejemplo siguiente declara dos interfaces.

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

};
      

Declarar clases

Una clase se comporta de forma similar a las clases de C++. Una clase se declara con la palabra clave class y puede contener variables y métodos de miembro. Una clase puede heredar de cero o de una clase y cero o más interfaces. Las clases deben implementar o heredar implementaciones para todas las interfaces de su cadena de herencia o no se pueden crear instancias de la clase .

En el código del sombreador de ejemplo siguiente se muestra cómo derivar una clase de una interfaz y de otra clase.

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

Declaraciones de instancia de interfaz en un sombreador

Una instancia de interfaz actúa como un marcador de posición para las instancias de clase que proporcionan una implementación de los métodos de la interfaz. El uso de una instancia de una interfaz permite que el código del sombreador llame a un método sin saber qué implementación de ese método se invocará. El código del sombreador declara una o varias instancias para cada interfaz que define. Estas instancias se usan en el código de sombreador de forma similar a los punteros de clase base de C++.

En el código del sombreador de ejemplo siguiente se muestra cómo declarar varias instancias de interfaz y usarlas en el código del sombreador.

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

Declaraciones de instancia de clase en un sombreador

Cada clase que se usará en lugar de una instancia de interfaz debe declararse como una variable en un búfer de constantes o crearla la aplicación en tiempo de ejecución mediante el método ID3D11ClassLinkage::CreateClassInstance . Las instancias de interfaz se apuntarán a las instancias de clase en el código de la aplicación. Se puede hacer referencia a las instancias de clase en código de sombreador como cualquier otra variable, pero una clase derivada de una interfaz normalmente solo se usará con una instancia de interfaz y no se hará referencia directamente al código del sombreador.

El código del sombreador de ejemplo siguiente muestra la declaración de varias instancias de clase.

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

Inicialización de instancias de interfaz en una aplicación

Las instancias de interfaz se inicializan en el código de la aplicación pasando una matriz de vinculación dinámica que contiene asignaciones de interfaz a uno de los métodos ID3D11DeviceContext SetShader.

Para crear una matriz de vinculación dinámica, siga estos pasos:

  1. Cree un objeto de vinculación de clases mediante CreateClassLinkage.

    ID3D11ClassLinkage* g_pPSClassLinkage = NULL;            
    pd3dDevice->CreateClassLinkage( &g_pPSClassLinkage );
    
    
  2. Cree el sombreador que usará la vinculación de clases dinámicas y pase el objeto de vinculación de clase como parámetro a la función create del sombreador.

    pd3dDevice->CreatePixelShader( pPixelShaderBuffer->GetBufferPointer(),
        pPixelShaderBuffer->GetBufferSize(), g_pPSClassLinkage, &g_pPixelShader ) );            
    
    
  3. Cree un objeto ID3D11ShaderReflection mediante la función D3DReflect .

    ID3D11ShaderReflection* pReflector = NULL; 
    D3DReflect( pPixelShaderBuffer->GetBufferPointer(),                  
        pPixelShaderBuffer->GetBufferSize(), 
        IID_ID3D11ShaderReflection, (void**) &pReflector) );            
    
    
  4. Use el objeto de reflexión del sombreador para obtener el número de instancias de interfaz en el sombreador mediante el método ID3D11ShaderReflection::GetNumInterfaceSlots .

    g_iNumPSInterfaces = pReflector->GetNumInterfaceSlots();             
    
    
  5. Cree una matriz lo suficientemente grande como para contener el número de instancias de interfaz en el sombreador.

    ID3D11ClassInstance** g_dynamicLinkageArray = NULL;            
    g_dynamicLinkageArray = 
        (ID3D11ClassInstance**) malloc( sizeof(ID3D11ClassInstance*) * g_iNumPSInterfaces );            
    
    
  6. Determine el índice de la matriz que corresponde a cada instancia de interfaz mediante ID3D11ShaderReflection::GetVariableByName y ID3D11ShaderReflectionVariable::GetInterfaceSlot.

    ID3D11ShaderReflectionVariable* pAmbientLightingVar = 
        pReflector->GetVariableByName("g_abstractAmbientLighting");
        g_iAmbientLightingOffset = pAmbientLightingVar->GetInterfaceSlot(0);            
    
    
  7. Obtiene una instancia de clase para cada objeto de clase derivado de una interfaz del sombreador mediante ID3D11ClassLinkage::GetClassInstance.

    g_pPSClassLinkage->GetClassInstance( "g_hemiAmbientLight", 0, 
        &g_pHemiAmbientLightClass );            
    
    
  8. Establezca instancias de interfaz en instancias de clase estableciendo la entrada correspondiente en la matriz de vinculación dinámica.

    g_dynamicLinkageArray[g_iAmbientLightingOffset] = g_pHemiAmbientLightClass;            
    
    
  9. Pase la matriz de vinculación dinámica como parámetro a una llamada a SetShader.

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

Vinculación dinámica