Schnittstellen und Klassen
Dynamische Shaderverknüpfung verwendet Schnittstellen und Klassen mit hoher Shadersprache (High Level Shader Language, HLSL), die syntaktisch ihren C++-Entsprechungen ähneln. Dadurch können Shader zur Kompilierungszeit auf abstrakte Schnittstelleninstanzen verweisen und die Auflösung dieser Instanzen auf konkrete Klassen für die Anwendung zur Laufzeit verlassen.
In den folgenden Abschnitten wird beschrieben, wie Sie einen Shader einrichten, um Schnittstellen und Klassen zu verwenden, und wie Schnittstelleninstanzen im Anwendungscode initialisiert werden.
- Deklarieren von Schnittstellen
- Deklarieren von Klassen
- Schnittstelleninstanzdeklarationen in einem Shader-
- Klasseninstanzdeklarationen in einem Shader-
- Initialisieren von Schnittstelleninstanzen in einer Anwendung
- Verwandte Themen
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 Von der Schnittstelle abgeleiteten Klassen. Abgeleitete Klassen müssen alle in einer Schnittstelle deklarierten Methoden implementieren. Beachten Sie, dass Schnittstellen die einzige Möglichkeit zum Deklarieren virtueller Methoden sind, es gibt kein virtuelles Schlüsselwort wie in C++, und Klassen deklarieren virtuelle Methoden.
Im folgenden Beispiel-Shadercode werden zwei Schnittstellen deklariert.
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 der Vererbungskette implementieren oder erben, oder die Klasse kann nicht instanziiert werden.
Im folgenden Beispiel-Shadercode wird veranschaulicht, wie eine Klasse von 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. Durch die Verwendung 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 von ihr definierte Schnittstelle. Diese Instanzen werden in Shadercode auf ähnliche Weise wie C++-Basisklassenzeiger verwendet.
Im folgenden Beispiel-Shadercode wird das Deklarieren mehrerer Schnittstelleninstanzen und die Verwendung in Shadercode veranschaulicht.
// 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 wie jede andere Variable im Shadercode referenziert werden. Eine von einer Schnittstelle abgeleitete Klasse wird in der Regel nur mit einer Schnittstelleninstanz verwendet und wird nicht direkt vom Shadercode referenziert.
Im folgenden Beispiel-Shadercode wird das Deklarieren mehrerer Klasseninstanzen veranschaulicht.
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 ID3D11DeviceContext- SetShader-Methoden übergeben wird.
Zum Erstellen eines dynamischen Verknüpfungsarrays führen Sie die folgenden Schritte aus:
Erstellen Sie ein Klassenverknüpfungsobjekt mit CreateClassLinkage.
ID3D11ClassLinkage* g_pPSClassLinkage = NULL; pd3dDevice->CreateClassLinkage( &g_pPSClassLinkage );
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 ) );
Erstellen Sie ein ID3D11ShaderReflection--Objekt mithilfe der D3DReflect-Funktion.
ID3D11ShaderReflection* pReflector = NULL; D3DReflect( pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(), IID_ID3D11ShaderReflection, (void**) &pReflector) );
Verwenden Sie das Shaderreflektionsobjekt, um die Anzahl der Schnittstelleninstanzen im Shader mithilfe der ID3D11ShaderReflection::GetNumInterfaceSlots-Methode abzurufen.
g_iNumPSInterfaces = pReflector->GetNumInterfaceSlots();
Erstellen Sie ein Array, das groß genug ist, um die Anzahl der Schnittstelleninstanzen im Shader aufzunehmen.
ID3D11ClassInstance** g_dynamicLinkageArray = NULL; g_dynamicLinkageArray = (ID3D11ClassInstance**) malloc( sizeof(ID3D11ClassInstance*) * g_iNumPSInterfaces );
Bestimmen Sie den Index im Array, der jeder Schnittstelleninstanz entspricht, mithilfe ID3D11ShaderReflection::GetVariableByName und ID3D11ShaderReflectionVariable::GetInterfaceSlot.
ID3D11ShaderReflectionVariable* pAmbientLightingVar = pReflector->GetVariableByName("g_abstractAmbientLighting"); g_iAmbientLightingOffset = pAmbientLightingVar->GetInterfaceSlot(0);
Rufen Sie eine Klasseninstanz für jedes Klassenobjekt ab, das von einer Schnittstelle im Shader abgeleitet wurde, mithilfe ID3D11ClassLinkage::GetClassInstance.
g_pPSClassLinkage->GetClassInstance( "g_hemiAmbientLight", 0, &g_pHemiAmbientLightClass );
Legen Sie Schnittstelleninstanzen auf Klasseninstanzen fest, indem Sie den entsprechenden Eintrag im dynamischen Verknüpfungsarray festlegen.
g_dynamicLinkageArray[g_iAmbientLightingOffset] = g_pHemiAmbientLightClass;
Übergeben Sie das dynamische Verknüpfungsarray als Parameter an einen SetShader-Aufruf.
pd3dImmediateContext->PSSetShader( g_pPixelShader, g_dynamicLinkageArray, g_iNumPSInterfaces );