Unity 导入FBX提取材质贴图

需求

当我们在建模软件制作好fbx,导入Unity时,会发现fbx有自带的材质,贴图,蒙皮,网格等多种资源,但是使用起来很不方便,所以想,在向unity导入fbx之后,直接就把里边的材质和贴图提取出来,并且不是默认的 Lit ,而是其它的自己写的shader

成果展示

FBX导入时提取材质

背景知识

当我们在Unity中选中一个fbx资产的时候,可以在右侧的检视面板看到这样的一些按钮
在这里插入图片描述
具体的解释可以看官方文档
https://docs.unity3d.com/cn/current/Manual/FBXImporter-Materials.html#material_description

其中我们比较关心的是:
Import via MaterialDescription (Experimental) 导入时,Unity 使用 FBX 文件中嵌入的材质描述来生成材质。与先前的导入方法相比,此方法可提供更准确的结果,并且支持更广泛的材质类型
Use External Materials (Legacy) 将导入的材质提取为外部资源。
Naming 定义材质的命名策略。
Search 定义 Unity 尝试在使用 Naming 选项定义的名称时查找现有材质的位置。

另一部分需要了解的是Unity里的 AssetPostprocessor 类,这个类可以帮助我们实现一些,关于导入导出资产时的相关回调
比如说:

OnPreprocessModel() 在导入模型(.fbx、.mb 文件等)之前获取通知。

OnPostprocessModel() 在模型完成导入时获取通知

OnPreprocessMaterialDescription 这个函数是在把 ModelImporter.materialImportMode 设置为 ImportViaMaterialDescription时,Unity 才会调用此函数

还有其他的一些方法可以在官方文档中查看
https://docs.unity.cn/cn/current/ScriptReference/AssetPostprocessor.html

代码实现

 	List< string> materialName =new List<string>() { };
    Dictionary<string, string> matTextureMap = new Dictionary<string, string>() { };
	//当材质为通过description时调用 根据描述获得相关信息
    void OnPreprocessMaterialDescription(MaterialDescription description, Material material, AnimationClip[] materialAnimation)
    {      
        materialName.Add( description.materialName);

        List<string> props = new List<string>();
        description.GetTexturePropertyNames(props);

        TexturePropertyDescription textureProperty;
        foreach (string item in props)
        {
            if (description.TryGetProperty(item, out textureProperty))
            {
                if (!matTextureMap.ContainsKey(description.materialName))
                {
                    matTextureMap.Add(description.materialName, textureProperty.texture.name);
                }
            }
        }
        
    }
 void OnPreprocessModel()
    {   
        if (!assetPath.ToLower().EndsWith(".fbx"))
        {
            return;            
        }       

        ModelImporter mi = assetImporter as ModelImporter;
        //设置fbx ModelImporter的相关属性
        mi.materialImportMode = ModelImporterMaterialImportMode.ImportViaMaterialDescription;
        mi.materialLocation = ModelImporterMaterialLocation.External;
        mi.materialName = ModelImporterMaterialName.BasedOnMaterialName;
        mi.materialSearch = ModelImporterMaterialSearch.Everywhere;    
    }
//提取材质 设置shader属性 赋贴图
 void OnPostprocessModel(GameObject g)
    {
        foreach (string matName in materialName)
        {
            string matPath = assetPath.Substring(0, assetPath.LastIndexOf('/') + 1) + "Materials/" + matName + ".mat";

            Material m = (Material)AssetDatabase.LoadAssetAtPath(matPath, typeof(Material));

            Shader shader = Shader.Find("X51/透明顶点颜色");

            if (shader == null)
            {
                Debug.Log("没找到shader");
                return;
            }

            if (m != null)
            {
                m.shader = shader;
                if (matTextureMap.ContainsKey(matName))
                {
                    string textPath = assetPath.Substring(0, assetPath.LastIndexOf('/') + 1);
                    textPath += g.name + ".fbm/" + matTextureMap[matName] + ".dds";

                    Texture texture = (Texture)AssetDatabase.LoadAssetAtPath(textPath, typeof(Texture));

                    m.SetTexture("_Tex1", texture);                   
                }
            }
        }

使用方法

把写好的代码放在Unity的Editor文件夹下,就能直接拖入fbx,看到成果啦