In the pixel shader, we first define base interfaces for different light and material types:
//--------------------------------------------------------------------------------------
// 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();
};
// ...
Next we build specialized classes based on these interfaces, adding to the functionality where needed:
//--------------------------------------------------------------------------------------
// Classes
//--------------------------------------------------------------------------------------
class cAmbientLight : iBaseLight
{
float3 m_vLightColor;
bool m_bEnable;
float3 IlluminateAmbient(float3 vNormal);
float3 IlluminateDiffuse(float3 vNormal);
float3 IlluminateSpecular(float3 vNormal, int specularPower );
};
// ...
class cBaseMaterial : iBaseMaterial
{
float3 m_vColor;
int m_iSpecPower;
float3 GetAmbientColor(float2 vTexcoord);
float3 GetDiffuseColor(float2 vTexcoord);
int GetSpecularPower();
};
// ....
The pixel shaders main function uses abstract instances of the interfaces for computation. These interfaces instances are made concrete by the application code at shader bind time:
/--------------------------------------------------------------------------------------
// Abstract Interface Instances for dyamic linkage / permutation
//--------------------------------------------------------------------------------------
iBaseLight g_abstractAmbientLighting;
iBaseLight g_abstractDirectLighting;
iBaseMaterial g_abstractMaterial;
//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PSMain( PS_INPUT Input ) : SV_TARGET
{
// Compute the Ambient term
float3 Ambient = (float3)0.0f;
Ambient = g_abstractMaterial.GetAmbientColor( Input.vTexcoord ) * g_abstractAmbientLighting.IlluminateAmbient( Input.vNormal );
// Accumulate the Diffuse contribution
float3 Diffuse = (float3)0.0f;
Diffuse += g_abstractMaterial.GetDiffuseColor( Input.vTexcoord ) * g_abstractDirectLighting.IlluminateDiffuse( Input.vNormal );
// Compute the Specular contribution
float3 Specular = (float3)0.0f;
Specular += g_abstractDirectLighting.IlluminateSpecular( Input.vNormal, g_abstractMaterial.GetSpecularPower() );
// Accumulate the lighting with saturation
float3 Lighting = saturate( Ambient + Diffuse + Specular );
return float4(Lighting,1.0f);
}
// use shader reflection to get data locations for the interface array ID3D11ShaderReflection* pReflector = NULL;
//ID3D11shaderReflection是访问shader信息的一个interface; V_RETURN( D3DReflect( pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(),
//D3Dreflect函数用以create一个id3d11shaderreflection的interface; IID_ID3D11ShaderReflection, (void**) &pReflector) ); g_iNumPSInterfaces = pReflector->GetNumInterfaceSlots(); g_dynamicLinkageArray = (ID3D11ClassInstance**) malloc( sizeof(ID3D11ClassInstance*) * g_iNumPSInterfaces ); if (g_dynamicLinkageArray == NULL) return E_FAIL; ID3D11ShaderReflectionVariable* pAmbientLightingVar = pReflector->GetVariableByName("g_abstractAmbientLighting"); g_iAmbientLightingOffset = pAmbientLightingVar->GetInterfaceSlot(0); ID3D11ShaderReflectionVariable* pDirectLightingVar = pReflector->GetVariableByName("g_abstractDirectLighting"); g_iDirectLightingOffset = pDirectLightingVar->GetInterfaceSlot(0); ID3D11ShaderReflectionVariable* pMaterialVar = pReflector->GetVariableByName("g_abstractMaterial"); g_iMaterialOffset = pMaterialVar->GetInterfaceSlot(0); // ...
Next we enumerate all possible permutations of material object that exist in the shader:
// Material Dynamic Permutation
enum E_MATERIAL_TYPES
{
MATERIAL_PLASTIC,
MATERIAL_PLASTIC_TEXTURED,
MATERIAL_PLASTIC_LIGHTING_ONLY,
MATERIAL_ROUGH,
MATERIAL_ROUGH_TEXTURED,
MATERIAL_ROUGH_LIGHTING_ONLY,
MATERIAL_TYPE_COUNT
};
char* g_pMaterialClassNames[ MATERIAL_TYPE_COUNT ] =
{
"g_plasticMaterial", // cPlasticMaterial
"g_plasticTexturedMaterial", // cPlasticTexturedMaterial
"g_plasticLightingOnlyMaterial", // cPlasticLightingOnlyMaterial
"g_roughMaterial", // cRoughMaterial
"g_roughTexturedMaterial", // cRoughTexturedMaterial
"g_roughLightingOnlyMaterial" // cRoughLightingOnlyMaterial
};
E_MATERIAL_TYPES g_iMaterial = MATERIAL_PLASTIC_TEXTURED;
// ...
// Acquire the material Class Instances for all possible material settings
for( UINT i=0; i < MATERIAL_TYPE_COUNT; i++)
{
g_pPSClassLinkage->GetClassInstance( g_pMaterialClassNames[i], 0, &g_pMaterialClasses[i] );
}
shader中(如果显卡不支持DX11,则STATIC_PERMUTE为True):
#if !defined( STATIC_PERMUTE ) iBaseLight g_abstractAmbientLighting; ... iBaseMaterial g_abstractMaterial; #else ... #define g_abstractAmbientLighting g_ambientLight #define g_abstractDirectLighting g_directionalLight #define g_abstractEnvironmentLighting g_environmentLight #define g_abstractMaterial g_plasticMaterial #endif
App中(D3DX11CompileFromFile的第二个参数就是D3D10_SHADER_MACRO *pDefines,预定义列表。显然预定义改变,需重新编译):
-
// Compile the shader using optional defines and an include handler for header processing static const D3D_SHADER_MACRO Shader_Macros[] = { "STATIC_PERMUTE", "1", NULL, NULL }; ID3DBlob* pErrorBlob; hr = D3DX11CompileFromFile( szFileName, &Shader_Macros[0], pIncludeHandler, szEntryPoint, szShaderModel, flags, 0, NULL, ppBlobOut, &pErrorBlob, NULL ); ...
-
// Expand compressed vectors
tmpNormal = R10G10B10A2_UNORM_TO_R32G32B32_FLOAT( Input.vNormal );float3 R10G10B10A2_UNORM_TO_R32G32B32_FLOAT( in float3 vVec )
{
vVec *= 2.0f;
return vVec >= 1.0f ? ( vVec - 2.0f ) : vVec;
}
3.场景渲染时,动态指定父类指针实际子类实例。
shader:
//interface
interface iBaseMaterial
{
float3 GetAmbientColor(float2 vTexcoord);
float3 GetDiffuseColor(float2 vTexcoord);
int GetSpecularPower();
};
//class
class cBaseMaterial:iBaseMaterial
{
float3 m_color;
int m_specularPower;
float3 GetAmbientColor(float2 vTexcoord){return m_color}
float3 GetDiffuseColor(float2 vTexcoord){return m_color}
int GetSpecularPower(){return m_specularPower}
};
//////////////////////////PlasticMaterial/////////////////////////////
class cPlasticMaterial:cBaseMaterial
{};
class cPlasticTextureMaterial:cPlasticMaterial
{
float3 GetAmbientColor(float2 vTexcoord);
float3 GetDiffuseColor(float2 vTexcoord);
}
class cPlasticLightingOnlyMaterial : cBaseMaterial
{
float3 GetAmbientColor(float2 vTexcoord)
{
return (float3)1.0f;
}
float3 GetDiffuseColor(float2 vTexcoord)
{
return (float3)1.0f;
}
};
//////////////////////////RoughMaterial//////////////////////
class cRoughMaterial : cBaseMaterial
{
int GetSpecularPower()
{
return m_iSpecPower;
}
};
class cRoughTexturedMaterial : cRoughMaterial
{
float3 GetAmbientColor(float2 vTexcoord);
float3 GetDiffuseColor(float2 vTexcoord);
};
class cRoughLightingOnlyMaterial : cRoughMaterial
{
float3 GetAmbientColor(float2 vTexcoord)
{
return (float3)1.0f;
}
float3 GetDiffuseColor(float2 vTexcoord)
{
return (float3)1.0f;
}
};
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() );
Specular += g_abstractEnvironmentLighting.IlluminateSpecular( Input.vNormal, g_abstractMaterial.GetSpecularPower() );
float3 Lighting = saturate( Ambient + Diffuse + Specular );
return float4(Lighting,1.0f);
}
App中:
设置shader中声明的Interface的具体子类实例。
// 初始化阶段, 相当于初始化shader中各interface子类实例
ID3D11ClassLinkage* g_pPSClassLinkage= NULL;
truct CB_PS_PER_PRIMITIVE
{ D3DXVECTOR4 m_vObjectColorPlastic; // Plastic -.w is Specular Power
D3DXVECTOR4 m_vObjectColorPlasticTextured; // Plastic -.w is Specular Power
D3DXVECTOR4 m_vObjectColorPlasticLightingOnly; // Plastic - Lighting Only
D3DXVECTOR4 m_vObjectColorRough; // Rough Material -.w is Specular Power
D3DXVECTOR4 m_vObjectColorRoughTextured; // Rough Material -.w is Specular Power
D3DXVECTOR4 m_vObjectColorRoughLightingOnly; // Rough Material -.w is Specular Power
};
enum E_MATERIAL_TYPES
{
MATERIAL_PLASTIC,
MATERIAL_PLASTIC_TEXTURED,
MATERIAL_PLASTIC_LIGHTING_ONLY,
MATERIAL_ROUGH,
MATERIAL_ROUGH_TEXTURED,
MATERIAL_ROUGH_LIGHTING_ONLY,
MATERIAL_TYPE_COUNT
};
char* g_pMaterialClassNames[ MATERIAL_TYPE_COUNT ] =
{
"g_plasticMaterial", // cPlasticMaterial
"g_plasticTexturedMaterial", // cPlasticTexturedMaterial
"g_plasticLightingOnlyMaterial", // cPlasticLightingOnlyMaterial
"g_roughMaterial", // cRoughMaterial
"g_roughTexturedMaterial", // cRoughTexturedMaterial
"g_roughLightingOnlyMaterial" // cRoughLightingOnlyMaterial
};
// use shader reflection to get data locations for the interface array
//shader中的metadata可以被reflection调用ID3D11ShaderReflection* pReflector = NULL;
V_RETURN( D3DReflect( pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(), IID_ID3D11ShaderReflection, (void**) &pReflector) );
g_iNumPSInterfaces = pReflector->GetNumInterfaceSlots();