Loading

UE 中默认材质初始化

UE 中如果发生材质丢失或者材质未指定时,会使用引擎初始化时创建的默认材质,下面时默认材质的创建流程:

默认材质的创建实现在UMaterialInterface::InitDefaultMaterials()中:

class UMaterialInterface : public UObject, public IBlendableInterface, public IInterface_AssetUserData
{	
	/**
	 * Initializes all default materials.
	 */
	ENGINE_API static void InitDefaultMaterials();
};

实现如下:

void UMaterialInterface::InitDefaultMaterials()
{
   // Note that this function will (in fact must!) be called recursively. This
   // guarantees that the default materials will have been loaded and pointers
   // set before any other material interface has been instantiated -- even
   // one of the default materials! It is actually possible to assert that
   // these materials exist in the UMaterial or UMaterialInstance constructor.
   // 
   // The check for initialization is purely an optimization as initializing
   // the default materials is only done very early in the boot process.
   static bool bInitialized = false;
   if (!bInitialized)
   {
      SCOPED_BOOT_TIMING("UMaterialInterface::InitDefaultMaterials");
      check(IsInGameThread());
      if (!IsInGameThread())
      {
         return;
      }
      static int32 RecursionLevel = 0;
      RecursionLevel++;

      
#if WITH_EDITOR
      GPowerToRoughnessMaterialFunction = LoadObject< UMaterialFunction >(nullptr, TEXT("/Engine/Functions/Engine_MaterialFunctions01/Shading/PowerToRoughness.PowerToRoughness"), nullptr, LOAD_None, nullptr);
      checkf( GPowerToRoughnessMaterialFunction, TEXT("Cannot load PowerToRoughness") );
      GPowerToRoughnessMaterialFunction->AddToRoot();

      GConvertFromDiffSpecMaterialFunction = LoadObject< UMaterialFunction >(nullptr, TEXT("/Engine/Functions/Engine_MaterialFunctions01/Shading/ConvertFromDiffSpec.ConvertFromDiffSpec"), nullptr, LOAD_None, nullptr);
      checkf( GConvertFromDiffSpecMaterialFunction, TEXT("Cannot load ConvertFromDiffSpec") );
      GConvertFromDiffSpecMaterialFunction->AddToRoot();
#endif

      for (int32 Domain = 0; Domain < MD_MAX; ++Domain)
      {
         if (GDefaultMaterials[Domain] == nullptr)
         {
            FString ResolvedPath = ResolveIniObjectsReference(GDefaultMaterialNames[Domain]);

            GDefaultMaterials[Domain] = FindObject<UMaterial>(nullptr, *ResolvedPath);
            if (GDefaultMaterials[Domain] == nullptr
#if USE_EVENT_DRIVEN_ASYNC_LOAD_AT_BOOT_TIME
               && (RecursionLevel == 1 || !GEventDrivenLoaderEnabled)
#endif
               )
            {
               GDefaultMaterials[Domain] = LoadObject<UMaterial>(nullptr, *ResolvedPath, nullptr, LOAD_DisableDependencyPreloading, nullptr);
               checkf(GDefaultMaterials[Domain] != nullptr, TEXT("Cannot load default material '%s'"), GDefaultMaterialNames[Domain]);
            }
            if (GDefaultMaterials[Domain])
            {
               GDefaultMaterials[Domain]->AddToRoot();
            }
         }
      }
      
      RecursionLevel--;
#if USE_EVENT_DRIVEN_ASYNC_LOAD_AT_BOOT_TIME
      bInitialized = !GEventDrivenLoaderEnabled || RecursionLevel == 0;
#else
      bInitialized = true;
#endif
   }
}

其中EMaterialDomain定义如下,

/** Defines the domain of a material. */
UENUM()
enum EMaterialDomain
{
	/** The material's attributes describe a 3d surface. */
	MD_Surface UMETA(DisplayName = "Surface"),
	/** The material's attributes describe a deferred decal, and will be mapped onto the decal's frustum. */
	MD_DeferredDecal UMETA(DisplayName = "Deferred Decal"),
	/** The material's attributes describe a light's distribution. */
	MD_LightFunction UMETA(DisplayName = "Light Function"),
	/** The material's attributes describe a 3d volume. */
	MD_Volume UMETA(DisplayName = "Volume"),
	/** The material will be used in a custom post process pass. */
	MD_PostProcess UMETA(DisplayName = "Post Process"),
	/** The material will be used for UMG or Slate UI */
	MD_UI UMETA(DisplayName = "User Interface"),
	/** The material will be used for runtime virtual texture (Deprecated). */
	MD_RuntimeVirtualTexture UMETA(Hidden),

	MD_MAX
};

在下面的这个循环中,遍历Material 的 所有的Domain,对不同类型的默认材质进行初始化

for (int32 Domain = 0; Domain < MD_MAX; ++Domain)

然后从GDefaultMaterialNames取得默认材质的指定路径,GDefaultMaterialNames定义如下

static const TCHAR* GDefaultMaterialNames[MD_MAX] =
{
	// Surface
	TEXT("engine-ini:/Script/Engine.Engine.DefaultMaterialName"),
	// Deferred Decal
	TEXT("engine-ini:/Script/Engine.Engine.DefaultDeferredDecalMaterialName"),
	// Light Function
	TEXT("engine-ini:/Script/Engine.Engine.DefaultLightFunctionMaterialName"),
	// Volume
	//@todo - get a real MD_Volume default material
	TEXT("engine-ini:/Script/Engine.Engine.DefaultMaterialName"),
	// Post Process
	TEXT("engine-ini:/Script/Engine.Engine.DefaultPostProcessMaterialName"),
	// User Interface 
	TEXT("engine-ini:/Script/Engine.Engine.DefaultMaterialName"),
	// Virtual Texture
	TEXT("engine-ini:/Script/Engine.Engine.DefaultMaterialName")
};

最后从指定路径中导入默认材质,并做AddToRoot操作防止垃圾回收。

默认材质什么时候初始化需要进行断点查看,如果不使用默认材质,应该会考虑到节约内存而延迟加载的情况。

posted @ 2022-02-09 11:32  straywriter  阅读(520)  评论(0编辑  收藏  举报