Interfaces en klassen
Dynamische shaderkoppeling maakt gebruik van HLSL-interfaces (High Level Shader Language) en -klassen die syntactisch vergelijkbaar zijn met hun C++-tegenhangers. Hierdoor kunnen shaders tijdens het compileren verwijzen naar abstracte interface-exemplaren en de oplossing van deze exemplaren laten staan aan concrete klassen voor de toepassing tijdens runtime.
In de volgende secties wordt beschreven hoe u een shader instelt voor het gebruik van interfaces en klassen en het initialiseren van interface-exemplaren in toepassingscode.
- interfaces declareren
- klassen declareren
- Declaraties van interface-exemplaren in een Shader-
- Declaraties van klasse-exemplaren in een Shader-
- interface-exemplaren initialiseren in een toepassings-
- Verwante onderwerpen
Interfaces declareren
Een interface werkt op een vergelijkbare manier als een abstracte basisklasse in C++. Een interface wordt gedeclareerd in een shader met behulp van het trefwoord interface en bevat alleen methodedeclaraties. De methoden die in een interface zijn gedeclareerd, zijn allemaal virtuele methoden in alle klassen die zijn afgeleid van de interface. Afgeleide klassen moeten alle methoden implementeren die zijn gedeclareerd in een interface. Interfaces zijn de enige manier om virtuele methoden te declareren, er is geen virtueel trefwoord zoals in C++, en klassen declareren virtuele methoden.
De volgende voorbeeld-shader-code declareert twee 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();
};
Klassen declareren
Een klasse gedraagt zich op een vergelijkbare manier als klassen in C++. Een klasse wordt gedeclareerd met het trefwoord klasse en kan lidvariabelen en -methoden bevatten. Een klasse kan overnemen van nul of één klasse en nul of meer interfaces. Klassen moeten implementaties implementeren of overnemen voor alle interfaces in de overnameketen of de klasse kan niet worden geïnstantieerd.
De volgende voorbeeld-shader-code illustreert het afleiden van een klasse uit een interface en van een andere klasse.
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);
};
Declaraties van interface-exemplaren in een shader
Een interface-exemplaar fungeert als een tijdelijke aanduiding voor klasse-exemplaren die een implementatie van de methoden van de interface bieden. Met behulp van een exemplaar van een interface kan shader-code een methode aanroepen zonder te weten welke implementatie van die methode wordt aangeroepen. Shader-code declareert een of meer exemplaren voor elke interface die wordt gedefinieerd. Deze exemplaren worden op een vergelijkbare manier gebruikt in shader-code als C++-basisklassepointers.
De volgende voorbeeld-shader-code illustreert het declareren van verschillende interface-exemplaren en het gebruik ervan in shader-code.
// 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);
}
Declaraties van klasse-exemplaren in een shader
Elke klasse die wordt gebruikt in plaats van een interface-exemplaar, moet worden gedeclareerd als een variabele in een constante buffer of worden gemaakt door de toepassing tijdens runtime met behulp van de ID3D11ClassLinkage::CreateClassInstance methode. Interface-exemplaren worden verwezen naar klasse-exemplaren in de toepassingscode. Er kan naar klasse-exemplaren worden verwezen in shader-code zoals elke andere variabele, maar een klasse die is afgeleid van een interface, wordt doorgaans alleen gebruikt met een interface-exemplaar en wordt niet rechtstreeks door shader-code verwezen.
De volgende voorbeeld-shader-code illustreert het declareren van verschillende klasse-exemplaren.
cbuffer cbPerFrame : register( b0 )
{
cAmbientLight g_ambientLight;
cHemiAmbientLight g_hemiAmbientLight;
cDirectionalLight g_directionalLight;
cEnvironmentLight g_environmentLight;
float4 g_vEyeDir;
};
Interface-exemplaren in een toepassing initialiseren
Interface-exemplaren worden geïnitialiseerd in toepassingscode door een dynamische koppelingsmatrix met interfacetoewijzingen door te geven aan een van de ID3D11DeviceContext SetShader-methoden.
Gebruik de volgende stappen om een dynamische koppelingsmatrix te maken
Maak een klassekoppelingsobject met behulp van CreateClassLinkage-.
ID3D11ClassLinkage* g_pPSClassLinkage = NULL; pd3dDevice->CreateClassLinkage( &g_pPSClassLinkage );
Maak de shader die dynamische klassekoppeling gebruikt, waarbij het klassekoppelingsobject als parameter wordt doorgegeven aan de create-functie van de shader.
pd3dDevice->CreatePixelShader( pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(), g_pPSClassLinkage, &g_pPixelShader ) );
Maak een ID3D11ShaderReflection-object met behulp van de functie D3DReflect.
ID3D11ShaderReflection* pReflector = NULL; D3DReflect( pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(), IID_ID3D11ShaderReflection, (void**) &pReflector) );
Gebruik het arceringsobject om het aantal interface-exemplaren in de shader op te halen met behulp van de methode ID3D11ShaderReflection::GetNumInterfaceSlots.
g_iNumPSInterfaces = pReflector->GetNumInterfaceSlots();
Maak een matrix die groot genoeg is om het aantal interface-exemplaren in de shader te bewaren.
ID3D11ClassInstance** g_dynamicLinkageArray = NULL; g_dynamicLinkageArray = (ID3D11ClassInstance**) malloc( sizeof(ID3D11ClassInstance*) * g_iNumPSInterfaces );
Bepaal de index in de matrix die overeenkomt met elk interface-exemplaar met behulp van ID3D11ShaderReflection::GetVariableByName en ID3D11ShaderReflectionVariable::GetInterfaceSlot.
ID3D11ShaderReflectionVariable* pAmbientLightingVar = pReflector->GetVariableByName("g_abstractAmbientLighting"); g_iAmbientLightingOffset = pAmbientLightingVar->GetInterfaceSlot(0);
Haal een klasse-exemplaar op voor elk klasseobject dat is afgeleid van een interface in de shader met behulp van ID3D11ClassLinkage::GetClassInstance.
g_pPSClassLinkage->GetClassInstance( "g_hemiAmbientLight", 0, &g_pHemiAmbientLightClass );
Stel interface-exemplaren in op klasse-exemplaren door de bijbehorende vermelding in de dynamische koppelingsmatrix in te stellen.
g_dynamicLinkageArray[g_iAmbientLightingOffset] = g_pHemiAmbientLightClass;
Geef de dynamische koppelingsmatrix door als een parameter aan een SetShader-aanroep.
pd3dImmediateContext->PSSetShader( g_pPixelShader, g_dynamicLinkageArray, g_iNumPSInterfaces );