欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

UE4 材质变体研究

程序员文章站 2022-06-10 20:52:43
...

在编辑器打开时,会从资源包中LoadPackage,我们这里只关心的是材质问题。每一个Obj,都会调用自己的Obj->ConditionalPostLoad() 这里已经是在加载的后端了,在这里面会调用子类的PostLoad()

for (int32 i = 0; i < ObjLoaded.Num(); i++)
{
        UObject* Obj = ObjLoaded[i];
        check(Obj);
#if WITH_EDITOR
//一顿操作
#endif
//又一顿操作
        Obj->ConditionalPostLoad();
}

 

我们只看 UMaterial::PostLoad,如果调用到这里,也就是说我们加载的是一个材质。这是一个非常长的函数。就不在这里 Super::PostLoad();

这里会调用他父类的PostLoad,而UMaterial的父类就是UMaterialInterface.

在UMaterialInterface::PostLoad()中又会调用UMaterial::CacheResourceShadersForRendering

  • Cache resource shaders for rendering.
  • If a matching shader map is not found in memory or the DDC, a new one will be compiled.(关于什么是DDC,听过很多次)
  • The results will be applied to this FMaterial in the renderer when they are finished compiling.
  • Note: This modifies material variables used for rendering and is assumed to be called within a FMaterialUpdateContext!
RebuildShadingModelField();//我们首先把ShadingModel重新计算,
FlushResourceShaderMaps();//将我们最终要渲染的MaterialResources,进行卸载
/*Only cache shaders for the quality level that will actually be used to render In cooked build, there is no shader compilation but this is still neededto register the loaded shadermap
//这里是只对我们将渲染到的level进行cache,在cooked build中,没有着色器编译,但这仍然是注册加载的shadermap(啥?)
CacheShadersForResources(ShaderPlatform, ResourcesToCache, true);
CacheShadersForResources这是最主要的函数,我们将要进去,看它是如何进行cache,并且看它都cache的什么东西。

void UMaterial::CacheShadersForResources(……)
{
    RebuildExpressionTextureReferences();//就按字面意思理解把
//对每一个将要resource
    for (int32 ResourceIndex = 0; ResourceIndex < ResourcesToCache.Num(); ResourceIndex++)
    {
        FMaterialResource CurrentResource = ResourcesToCache[ResourceIndex];
//最终cache的地方
        const bool bSuccess = CurrentResource->CacheShaders(ShaderPlatform, bApplyCompletedShaderMapForRendering);
        if (!bSuccess)
        {
//如果出现错误的log
        }
    }
}

从上面可以看出,我们最终的cache还是要在回到FMaterialResource身上,这里只是做了保护而已,所以继续往下。

bool FMaterial::CacheShaders(……)
{
        FMaterialShaderMapId NoStaticParametersId;
        GetShaderMapId(Platform, NoStaticParametersId);
        return CacheShaders(NoStaticParametersId, Platform, bApplyCompletedShaderMapForRendering);
}

这里我们终于来到了一个新的天地,因为我们终于不在Umaterial里面了,而是在FMaterialResource的父类FMaterial里面,

这里主要有两个重要函数

一个是FMaterialResource::GetShaderMapId

另一个是CacheShaders

FMaterialResource::GetShaderMapId

首先就是调用父类的
FMaterial::GetShaderMapId(Platform, OutId);
这里最终得到的是FMaterialShaderMapId
关于这个结构体和其他相关结构体的内容可以参见Material相关类 
GetShaderMapId:


if (bLoadedCookedShaderMapId)
{
  //开始做的判断是是否已经加载了这个material的shadermap,如果有的话我们还要判断是在哪个线程中就从GameThreadShaderMap获取还是RenderingThreadShaderMap获取。
}    
else
    {
#if WITH_EDITOR
        TArray<FShaderType> ShaderTypes;
        TArray<FVertexFactoryType> VFTypes;
        TArray<const FShaderPipelineType*> ShaderPipelineTypes;
        GetDependentShaderAndVFTypes(Platform, ShaderTypes, ShaderPipelineTypes, VFTypes);
    //省略赋值
        GetReferencedTexturesHash(Platform, OutId.TextureReferencesHash);
#else
   //省略赋值 log
#endif
    }

最开始做的判断是是否已经加载了这个material的shadermap,如果有的话我们还要判断是在哪个线程中就从GameThreadShaderMap获取还是RenderingThreadShaderMap获取。

GetDependentShaderAndVFTypes

否则的话,我们就需要进行ShaderMap的填充,GetDependentShaderAndVFTypes就是填充的关键。

  1. 对所有的VertexFactoryType进行判断
  2. 这个顶点工厂必须是使用在Material里的
  3. 从每一个FShaderType中得到FMeshMaterialShaderType,如果FMeshMaterialShaderType不是空&&FMeshMaterialShaderType可以cache这个VertexFactoryType,那么我们就允许增加这个AddUnique(ShaderType),并且在最后的时候会增加这个VertexFactoryType
  4. 从每一个FShaderPipelineType判断是否用于meshmaterial,并且要判断FShaderPipelineType里面所有的shadertype的,如果它里面的所有的shader都通过判断,那么我们就增加这个FShaderPipelineType
  5. 对所有的FShaderType进行判断
  6. 和上面的一样,不过判断条件VertexFactoryType改为了null
  7. 对所有的FShaderPipelineType进行判断
  8. 同上

其实我们只是对判断依据感兴趣而已。

  • todo:判断条件是传进来的函数指针,还没有搞清楚来源

这样,我们就讨论完了GetShaderMapId

FMaterialShaderMapId::SetShaderDependencies

下面的函数时FMaterialShaderMapId::SetShaderDependencies,主要就是记录它的hash值,然后记录下来,这里的疑问就出来了。

当我们的FMaterial::GetShaderMapId分析完后,我们就要回到 FMaterialResource::GetShaderMapId里面

//这是把所有的材质里使用到的函数,加到OutId.ReferencedFunctions中
Material->AppendReferencedFunctionIdsTo(OutId.ReferencedFunctions);
//这是把所有的材质里使用到的parameter collection,加到OutId.ReferencedParameterCollections中s
Material->AppendReferencedParameterCollectionIdsTo(OutId.ReferencedParameterCollections);
//添加texture的哈希值
Material->GetForceRecompileTextureIdsHash(OutId.TextureReferencesHash);
//如果其有关联的MaterialInstance不为空
if(MaterialInstance)
{
    MaterialInstance->GetBasePropertyOverridesHash(OutId.BasePropertyOverridesHash);
    FStaticParameterSet CompositedStaticParameters;
    MaterialInstance->GetStaticParameterValues(CompositedStaticParameters);
    OutId.UpdateParameterSet(CompositedStaticParameters);