如需转载本文,请声明作者及出处。
新增Shader时,通常需要用到以下的宏
DECLARE_SHADER_TYPE(FMobileXXXVS, MeshMaterial);
DECLARE_SHADER_TYPE(FMobileXXXPS, MeshMaterial);
IMPLEMENT_MATERIAL_SHADER_TYPE(, FMobileXXXVS, TEXT("/Engine/Private/XXXPassShader.usf"), TEXT("MainVS"), SF_Vertex);
IMPLEMENT_MATERIAL_SHADER_TYPE(, FMobileXXXPS, TEXT("/Engine/Private/XXXPassShader.usf"), TEXT("MainPS"), SF_Pixel);
IMPLEMENT_VERTEX_FACTORY_TYPE(FXXXVertexFactory, "/Engine/Private/XXXPassShader.usf", true, false, false, false, false);
才能实现c++类与Shader的绑定,才能在MeshPipeline中,AddMeshBatch时取到相应的shader的。
但在ue4中,是如何做到的呢?
Shader Permutation
1.几个关键宏的展开
先来看看DECLARE_SHADER_TYPE的展开
#define DECLARE_EXPORTED_SHADER_TYPE(ShaderClass,ShaderMetaTypeShortcut,RequiredAPI, ...) \
public: \
using FPermutationDomain = FShaderPermutationNone; \
using ShaderMetaType = F##ShaderMetaTypeShortcut##ShaderType; \
\
static RequiredAPI ShaderMetaType StaticType; \
\
static FShader* ConstructSerializedInstance() { return new ShaderClass(); } \
static FShader* ConstructCompiledInstance(const ShaderMetaType::CompiledShaderInitializerType& Initializer) \
{ return new ShaderClass(Initializer); } \
\
virtual uint32 GetTypeSize() const override { return sizeof(*this); }
以TMobileBasePassPS为例
template< typename LightMapPolicyType, EOutputFormat OutputFormat, bool bEnableSkyLight, bool bRenderRuntimeProceduralTexture, int32 NumMovablePointLights>
class TMobileBasePassPS : public TMobileBasePassPSBaseType<LightMapPolicyType>
{
DECLARE_SHADER_TYPE(TMobileBasePassPS,MeshMaterial);
}
会被展开为:
class TMobileBasePassPS : public TMobileBasePassPSBaseType<LightMapPolicyType>
{
using ShaderMetaType = FMeshMaterialShaderType;
static RequiredAPI ShaderMetaType StaticType;
}
如此,TMobileBasePassPS便有了ShaderMetaType 和 ShaderMetaType
而FShaderType是模板类的实例化类的静态成员,FShaderType构造函数里面会把自己注册到全局链表FShaderType::GetTypeList()里:
TLinkedList<FShaderType*>*& FShaderType::GetTypeList()
{
return GShaderTypeList;
}
FShaderType::FShaderType(...)
{
GlobalListLink.LinkHead(GetTypeList());
}
现在再来看看IMPLEMENT_MATERIAL_SHADER_TYPE的展开
#define IMPLEMENT_MATERIAL_SHADER_TYPE(TemplatePrefix,ShaderClass,SourceFilename,FunctionName,Frequency) \
IMPLEMENT_SHADER_TYPE( \
TemplatePrefix, \
ShaderClass, \
SourceFilename, \
FunctionName, \
Frequency \
);
#define IMPLEMENT_SHADER_TYPE(TemplatePrefix,ShaderClass,SourceFilename,FunctionName,Frequency) \
TemplatePrefix \
ShaderClass::ShaderMetaType ShaderClass::StaticType( \
TEXT(#ShaderClass), \
SourceFilename, \
FunctionName, \
Frequency, \
1, \
ShaderClass::ConstructSerializedInstance, \
ShaderClass::ConstructCompiledInstance, \
ShaderClass::ModifyCompilationEnvironment, \
ShaderClass::ShouldCompilePermutation, \
ShaderClass::ValidateCompiledResult \
);
所以,
IMPLEMENT_MATERIAL_SHADER_TYPE(, FMobileXXXPS, TEXT("/Engine/Private/XXXPassShader.usf"), TEXT("MainPS"), SF_Pixel);
将会被展开为:
FMobileXXXPS::ShaderMetaType FMobileXXXPS::StaticType("XXXPassShader.usf", "MainPS", SF_Pixel);
其中,FMobileXXXPS::ShaderMetaType FMobileXXXPS::StaticType 在DECLARE_SHADER_TYPE 展开时,已经指定。
所以,这也是为什么使用定制自己的shader时要先
class FMobileXXXPS : public FMeshMaterialShader
{
DECLARE_SHADER_TYPE(FMobileXXXPS, MeshMaterial);
public:
...
}
再
IMPLEMENT_MATERIAL_SHADER_TYPE(, FMobileXXXPS, TEXT("/Engine/Private/XXXPassShader.usf"), TEXT("MainPS"), SF_Pixel);
2.Shader Permutation
但以上只是讨论了几个宏的展开,没有涉及shader的变体,在ue4中,Shader Permutation又是如何运行的呢?
同样以TMobileBasePassPS 为例:
template< typename LightMapPolicyType, EOutputFormat OutputFormat, bool bEnableSkyLight, bool bRenderRuntimeProceduralTexture, int32 NumMovablePointLights>
class TMobileBasePassPS : public TMobileBasePassPSBaseType<LightMapPolicyType>
{
...
}
可以看到TMobileBasePassPS是一个模板类,有四个模板参数:
LightMapPolicyType,各种PolicyType在LightMapRendering.h中定义,如light map和shadow等的渲染,
引擎中Indirect Light Cache、VLM、Distance Field Shadow都有对应的LightMapPolicy类
OutputFormat,PS的输出是gamma的还是linear
bEnableSkyLight,是否开启SkyLight
NumMovablePointLights,PS里要处理的动态点光源的数目这些参数在GetUniformMobileBasePassShaders中使用
template <ELightMapPolicyType Policy, int32 NumMovablePointLights>
void GetUniformMobileBasePassShaders(
const FMaterial& Material,
FVertexFactoryType* VertexFactoryType,
bool bEnableSkyLight,
bool bRenderRuntimeProceduralTexture,
TMobileBasePassVSPolicyParamType<FUniformLightMapPolicy>*& VertexShader,
TMobileBasePassPSPolicyParamType<FUniformLightMapPolicy>*& PixelShader
)
{
if (IsMobileHDR())
{
VertexShader = (TMobileBasePassVSPolicyParamType<FUniformLightMapPolicy>*)Material.GetShader<TMobileBasePassVS<TUniformLightMapPolicy<Policy>, HDR_LINEAR_64> >(VertexFactoryType);
if (bEnableSkyLight)
{
if (bRenderRuntimeProceduralTexture)
{
PixelShader = (TMobileBasePassPSPolicyParamType<FUniformLightMapPolicy>*)Material.GetShader< TMobileBasePassPS<TUniformLightMapPolicy<Policy>, HDR_LINEAR_64, true, true, NumMovablePointLights> >(VertexFactoryType);
}
else
{
PixelShader = (TMobileBasePassPSPolicyParamType<FUniformLightMapPolicy>*)Material.GetShader< TMobileBasePassPS<TUniformLightMapPolicy<Policy>, HDR_LINEAR_64, true, false, NumMovablePointLights> >(VertexFactoryType);
}
}
else
{
if (bRenderRuntimeProceduralTexture)
{
PixelShader = (TMobileBasePassPSPolicyParamType<FUniformLightMapPolicy>*)Material.GetShader< TMobileBasePassPS<TUniformLightMapPolicy<Policy>, HDR_LINEAR_64, false, true, NumMovablePointLights> >(VertexFactoryType);
}
else
{
PixelShader = (TMobileBasePassPSPolicyParamType<FUniformLightMapPolicy>*)Material.GetShader< TMobileBasePassPS<TUniformLightMapPolicy<Policy>, HDR_LINEAR_64, false, false, NumMovablePointLights> >(VertexFactoryType);
}
}
}
else
{
VertexShader = (TMobileBasePassVSPolicyParamType<FUniformLightMapPolicy>*)Material.GetShader<TMobileBasePassVS<TUniformLightMapPolicy<Policy>, LDR_GAMMA_32> >(VertexFactoryType);
if (bEnableSkyLight)
{
if (bRenderRuntimeProceduralTexture)
{
PixelShader = (TMobileBasePassPSPolicyParamType<FUniformLightMapPolicy>*)Material.GetShader< TMobileBasePassPS<TUniformLightMapPolicy<Policy>, LDR_GAMMA_32, true, true, NumM