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(); }; // ....
Abstract Instances and The Shaders Main Function
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();