当前位置:网站首页>Unity 贴图自动匹配材质工具 贴图自动添加到材质球工具 材质球匹配贴图工具 Substance Painter制作的贴图自动匹配材质球工具
Unity 贴图自动匹配材质工具 贴图自动添加到材质球工具 材质球匹配贴图工具 Substance Painter制作的贴图自动匹配材质球工具
2022-07-07 10:03:00 【唐沢】
强大的Unity编辑器扩展
有的同学可能会问:模型制作好,在模型设置里先解压贴图,再解压材质不就行了?
当然可以,不过现在解决的是模型没有贴图,贴图都是在SP里做的,导出来都是单独的贴图,不会直接和模型绑定
总之,你自己一个一个往材质球上贴也没啥问题,我是觉得贴的太痛苦了,来一个模型我得拿来贴贴贴…贴个der.
编辑器扩展就能解决很多手动的问题.
先看效果:
材质球会自动匹配贴图
匹配贴图时需要注意:
贴图名称必须是:模型名称_材质球名称_贴图类型
属性介绍
- 模型路径:把模型拖入会自动识别路径
- 贴图后缀:类似_Albedo或者_Occlusion,有的同学喜欢这样命名,AL,AO,所以开放出来自定义
- 导出材质球:模型存在的目录会自动创建Material文件夹,未解压材质的模型将会把材质解压到此文件夹
- 设置材质:根据模型与材质球的名称自动匹配贴图
SP导出流程:
文件 - 导出贴图 - 配置里需要这样命名
其中:
- $project是当前SP项目名称(用不到)
- $mesh是模型名称
- $textureSet是材质球名称
代码介绍:
属性这里可以修改默认后缀
[SerializeField]
public string m_Albedo = "_Albedo", m_Metallic = "_Metallic", m_NormalMap = "_Normal", m_HeightMap = "_Height", m_OcclusionMap = "_Occlusion";
这里设置名称规则:objName + “_” + mat.name + m_Albedo,可以自行修改
void setMaterialShader(string path)
{
Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
string[] allPath = GetObjPath("Texture2D");
for (int i = 0, len = allPath.Length; i < len; i++)
{
string filePath = AssetDatabase.GUIDToAssetPath(allPath[i]);
Texture2D t = AssetDatabase.LoadAssetAtPath<Texture2D>(filePath);
string objName = Path.GetFileName(modelPath);
objName = objName.Substring(0, objName.Length - 4);
if (t.name == objName + "_" + mat.name + m_Albedo)
{
mat.SetTexture(Albedo, t);
}
else if (t.name == objName + "_" + mat.name + m_HeightMap)
{
mat.SetTexture(HeightMap, t);
}
else if (t.name == objName + "_" + mat.name + m_Metallic)
{
mat.SetTexture(Metallic, t);
}
else if (t.name == objName + "_" + mat.name + m_NormalMap)
{
mat.SetTexture(NormalMap, t);
}
else if (t.name == objName + "_" + mat.name + m_OcclusionMap)
{
mat.SetTexture(OcclusionMap, t);
}
else
{
Debug.Log(mat.name+"材质球无法匹配:" + t.name);
}
}
}
完整代码:
这个代码继承EditorWindow
所以必须放在Editor文件夹下, 制作旋转门与相机控制器也是一样,都是用的编辑器扩展技术
using System.IO;
using UnityEngine;
using UnityEditor;
public class MatchinMaterial_Editor : EditorWindow
{
public string modelPath = "Assets";
public Rect modelRect;
public string Albedo = "_MainTex";
public string Metallic = "_MetallicGlossMap";
public string NormalMap = "_BumpMap";
public string HeightMap = "_ParallaxMap";
public string OcclusionMap = "_OcclusionMap";
private static MatchinMaterial_Editor _window;
[SerializeField]
public string m_Albedo = "_Albedo", m_Metallic = "_Metallic", m_NormalMap = "_Normal", m_HeightMap = "_Height", m_OcclusionMap = "_Occlusion";
[MenuItem("Tools/材质匹配")]
public static void showWindow()
{
Rect wr = new Rect(0, 0, 300, 300);
// true 表示不能停靠的
_window = (MatchinMaterial_Editor)GetWindowWithRect(typeof(MatchinMaterial_Editor), wr, true, "材质匹配");
_window.Show();
}
public void OnGUI()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("模型路径 (鼠标拖拽文件夹到这里)");
EditorGUILayout.Space();
GUI.SetNextControlName("input1");//设置下一个控件的名字
modelRect = EditorGUILayout.GetControlRect();
modelPath = EditorGUI.TextField(modelRect, modelPath);
EditorGUILayout.Space();
m_Albedo = EditorGUILayout.TextField("Albedo图片后缀", m_Albedo);
m_Metallic = EditorGUILayout.TextField("Metallic图片后缀", m_Metallic);
m_NormalMap = EditorGUILayout.TextField("NormalMap图片后缀", m_NormalMap);
m_HeightMap = EditorGUILayout.TextField("HeightMap图片后缀", m_HeightMap);
m_OcclusionMap = EditorGUILayout.TextField("OcclusionMap图片后缀", m_OcclusionMap);
DragFolder();
EditorGUILayout.Space();
// 导出材质
if (GUILayout.Button("导出材质球"))
{
ForEachModels();
}
EditorGUILayout.Space();
if (GUILayout.Button("设置材质"))
{
ForEachMaterials();
}
}
/// <summary>
/// 获得拖拽文件
/// </summary>
void DragFolder()
{
//鼠标位于当前窗口
if (mouseOverWindow == this)
{
//拖入窗口未松开鼠标
if (Event.current.type == EventType.DragUpdated)
{
DragAndDrop.visualMode = DragAndDropVisualMode.Generic;//改变鼠标外观
// 判断区域
if (modelRect.Contains(Event.current.mousePosition))
GUI.FocusControl("input1");
}
//拖入窗口并松开鼠标
else if (Event.current.type == EventType.DragExited)
{
string dragPath = string.Join("", DragAndDrop.paths);
// 判断区域
if (modelRect.Contains(Event.current.mousePosition))
this.modelPath = dragPath;
// 取消焦点(不然GUI不会刷新)
GUI.FocusControl(null);
}
}
}
/// <summary>
/// 导出材质
/// </summary>
void ForEachModels()
{
string[] allPath = AssetDatabase.FindAssets("t:GameObject", new string[] {
modelPath });
//Debug.Log("-- allPath: " + allPath.Length);
for (int i = 0, len = allPath.Length; i < len; i++)
{
string filePath = AssetDatabase.GUIDToAssetPath(allPath[i]);
// 设置模型
ExtractMaterialsFromFBX(filePath);
}
// 如果选取的是FBX模型文件
if (allPath.Length == 0)
{
if (Path.GetExtension(modelPath) == ".fbx")
{
ExtractMaterialsFromFBX(modelPath);
}
else
{
Debug.LogError("当前选择目录未找到FBX文件: " + this.modelPath);
}
}
}
/// <summary>
/// 导出材质球
/// </summary>
/// <param name="assetPath"></param>
public void ExtractMaterialsFromFBX(string assetPath)
{
// 材质目录
string materialFolder = Path.GetDirectoryName(assetPath) + "/Material";
//Debug.Log(assetPath);
// 如果不存在该文件夹则创建一个新的
if (!AssetDatabase.IsValidFolder(materialFolder))
AssetDatabase.CreateFolder(Path.GetDirectoryName(assetPath), "Material");
// 获取 assetPath 下所有资源。
Object[] assets = AssetDatabase.LoadAllAssetsAtPath(assetPath);
foreach (Object item in assets)
{
if (item.GetType() == typeof(Material))
{
string path = System.IO.Path.Combine(materialFolder, item.name) + ".mat";
// 为资源创建一个新的唯一路径。
path = AssetDatabase.GenerateUniqueAssetPath(path);
// 通过在导入资源(例如,FBX 文件)中提取外部资源,在对象(例如,材质)中创建此资源。
string value = AssetDatabase.ExtractAsset(item, path);
// 成功提取( 如果 Unity 已成功提取资源,则返回一个空字符串)
if (string.IsNullOrEmpty(value))
{
AssetDatabase.WriteImportSettingsIfDirty(assetPath);
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
Debug.Log(Path.GetFileName(assetPath) + " 的 Material 导出成功!!");
}
}
}
}
/// <summary>
/// 得到所有材质球
/// </summary>
void ForEachMaterials()
{
string[] allPath = GetObjPath("Material");
for (int i = 0, len = allPath.Length; i < len; i++)
{
string filePath = AssetDatabase.GUIDToAssetPath(allPath[i]);
setMaterialShader(filePath);
}
Debug.Log("Material Shader 设置完成, 一共: " + allPath.Length + "个");
}
void setMaterialShader(string path)
{
Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
string[] allPath = GetObjPath("Texture2D");
for (int i = 0, len = allPath.Length; i < len; i++)
{
string filePath = AssetDatabase.GUIDToAssetPath(allPath[i]);
Texture2D t = AssetDatabase.LoadAssetAtPath<Texture2D>(filePath);
string objName = Path.GetFileName(modelPath);
objName = objName.Substring(0, objName.Length - 4);
if (t.name == objName + "_" + mat.name + m_Albedo)
{
mat.SetTexture(Albedo, t);
}
else if (t.name == objName + "_" + mat.name + m_HeightMap)
{
mat.SetTexture(HeightMap, t);
}
else if (t.name == objName + "_" + mat.name + m_Metallic)
{
mat.SetTexture(Metallic, t);
}
else if (t.name == objName + "_" + mat.name + m_NormalMap)
{
mat.SetTexture(NormalMap, t);
}
else if (t.name == objName + "_" + mat.name + m_OcclusionMap)
{
mat.SetTexture(OcclusionMap, t);
}
else
{
Debug.Log(mat.name+"材质球无法匹配:" + t.name);
}
}
}
string[] GetObjPath(string mType)
{
//string path = Path.GetDirectoryName(modelPath) + "/Material";//得到此物体的文件目录,具体到Material文件
string path = Path.GetDirectoryName(modelPath);//得到此物体的文件目录
return AssetDatabase.FindAssets("t:"+ mType, new string[] {
path });//在这个文件下找"t:Material"这个文件
}
}
这个代码需要Demo演示?
边栏推荐
- 问下flinkcdc2.2.0的版本,支持并发,这个并发是指多并行度吗,现在发现,mysqlcdc全
- 【数据聚类】基于多元宇宙优化DBSCAN实现数据聚类分析附matlab代码
- UP Meta—Web3.0世界创新型元宇宙金融协议
- SwiftUI 4 新功能之掌握 WeatherKit 和 Swift Charts
- Zhou Yajin, a top safety scholar of Zhejiang University, is a curiosity driven activist
- Camera calibration (2): summary of monocular camera calibration
- [Yugong series] go teaching course 005 variables in July 2022
- 顶级域名有哪些?是如何分类的?
- 超标量处理器设计 姚永斌 第8章 指令发射 摘录
- Use references
猜你喜欢
超标量处理器设计 姚永斌 第10章 指令提交 摘录
Fleet tutorial 19 introduction to verticaldivider separator component Foundation (tutorial includes source code)
SwiftUI Swift 内功之如何在 Swift 中进行自动三角函数计算
Talk about SOC startup (11) kernel initialization
About how to install mysql8.0 on the cloud server (Tencent cloud here) and enable local remote connection
Flet tutorial 17 basic introduction to card components (tutorial includes source code)
Fleet tutorial 15 introduction to GridView Basics (tutorial includes source code)
禁锢自己的因素,原来有这么多
Talk about SOC startup (VI) uboot startup process II
[filter tracking] strapdown inertial navigation simulation based on MATLAB [including Matlab source code 1935]
随机推荐
112.网络安全渗透测试—[权限提升篇10]—[Windows 2003 LPK.DDL劫持提权&msf本地提权]
相机标定(1): 单目相机标定及张正友标定基本原理
Talk about SOC startup (VI) uboot startup process II
Summed up 200 Classic machine learning interview questions (with reference answers)
Electron adding SQLite database
Unity中SmoothStep介绍和应用: 溶解特效优化
请查收.NET MAUI 的最新学习资源
R语言使用quantile函数计算评分值的分位数(20%、40%、60%、80%)、使用逻辑操作符将对应的分位区间(quantile)编码为分类值生成新的字段、strsplit函数将学生的名和姓拆分
【神经网络】卷积神经网络CNN【含Matlab源码 1932期】
超标量处理器设计 姚永斌 第10章 指令提交 摘录
《通信软件开发与应用》课程结业报告
5V串口接3.3V单片机串口怎么搞?
Basic introduction to the 16 tabs tab control in the fleet tutorial (the tutorial includes source code)
Explore cloud database of cloud services together
EasyUI learn to organize notes
【最短路】ACwing 1127. 香甜的黄油(堆优化的dijsktra或spfa)
[data clustering] realize data clustering analysis based on multiverse optimization DBSCAN with matlab code
Sonar:Cognitive Complexity认知复杂度
Common SQL statement collation: MySQL
R语言使用magick包的image_mosaic函数和image_flatten函数把多张图片堆叠在一起形成堆叠组合图像(Stack layers on top of each other)