인터페이스 및 클래스
동적 셰이더 연결은 C++ 대응 항목과 구문적으로 유사한 HLSL(High-Level Shader Language) 인터페이스 및 클래스를 사용합니다. 이를 통해 셰이더는 컴파일 시간에 추상 인터페이스 인스턴스를 참조하고 런타임에 해당 인스턴스의 해상도를 애플리케이션의 구체적인 클래스에 남겨둘 수 있습니다.
다음 섹션에서는 인터페이스와 클래스를 사용하도록 셰이더를 설정하는 방법과 애플리케이션 코드에서 인터페이스 인스턴스를 초기화하는 방법을 자세히 설명합니다.
인터페이스 선언
인터페이스는 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 키워드로 선언되며 멤버 변수 및 메서드를 포함할 수 있습니다. 클래스는 0개 또는 1개의 클래스와 0개 이상의 인터페이스에서 상속할 수 있습니다. 클래스는 상속 체인의 모든 인터페이스에 대한 구현을 구현하거나 상속해야 합니다. 그렇지 않으면 클래스를 인스턴스화할 수 없습니다.
다음 예 셰이더 코드는 인터페이스 및 다른 클래스에서 클래스를 파생시키는 것을 보여 줍니다.
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 메서드 중 하나에 인터페이스 할당이 포함된 동적 연결 배열을 전달하여 애플리케이션 코드에서 초기화됩니다.
동적 연결 배열을 만들려면 다음 단계를 사용합니다.
CreateClassLinkage를 사용하여 클래스 연결 개체를 만듭니다.
ID3D11ClassLinkage* g_pPSClassLinkage = NULL; pd3dDevice->CreateClassLinkage( &g_pPSClassLinkage );
동적 클래스 연결을 사용할 셰이더를 만들고 클래스 연결 개체를 매개 변수로 셰이더의 만들기 함수에 전달합니다.
pd3dDevice->CreatePixelShader( pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(), g_pPSClassLinkage, &g_pPixelShader ) );
D3DReflect 함수를 사용하여 ID3D11ShaderReflection 개체를 만듭니다.
ID3D11ShaderReflection* pReflector = NULL; D3DReflect( pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(), IID_ID3D11ShaderReflection, (void**) &pReflector) );
셰이더 반사 개체를 사용하여 ID3D11ShaderReflection::GetNumInterfaceSlots 메서드를 사용하여 셰이더의 인터페이스 인스턴스 수를 가져옵니다.
g_iNumPSInterfaces = pReflector->GetNumInterfaceSlots();
셰이더의 인터페이스 인스턴스 수를 담기에 충분히 큰 배열을 만듭니다.
ID3D11ClassInstance** g_dynamicLinkageArray = NULL; g_dynamicLinkageArray = (ID3D11ClassInstance**) malloc( sizeof(ID3D11ClassInstance*) * g_iNumPSInterfaces );
ID3D11ShaderReflection::GetVariableByName 및 ID3D11ShaderReflectionVariable::GetInterfaceSlot을 사용하여 각 인터페이스 인스턴스에 해당하는 배열의 인덱스를 결정합니다.
ID3D11ShaderReflectionVariable* pAmbientLightingVar = pReflector->GetVariableByName("g_abstractAmbientLighting"); g_iAmbientLightingOffset = pAmbientLightingVar->GetInterfaceSlot(0);
ID3D11ClassLinkage::GetClassInstance를 사용하여 셰이더의 인터페이스에서 파생된 각 클래스 개체에 대한 클래스 인스턴스를 가져옵니다.
g_pPSClassLinkage->GetClassInstance( "g_hemiAmbientLight", 0, &g_pHemiAmbientLightClass );
동적 연결 배열에서 해당 항목을 설정하여 인터페이스 인스턴스를 클래스 인스턴스로 설정합니다.
g_dynamicLinkageArray[g_iAmbientLightingOffset] = g_pHemiAmbientLightClass;
SetShader 호출에 대한 매개 변수로 동적 연결 배열을 전달합니다.
pd3dImmediateContext->PSSetShader( g_pPixelShader, g_dynamicLinkageArray, g_iNumPSInterfaces );