using UnityEngine; using UnityEditor; using System.IO; using System.Collections.Generic; public class TextureMergerWindow : EditorWindow { // 可选的目标合并贴图分辨率选项 public enum MergeResolution { _1024 = 1024, _2048 = 2048, _4096 = 4096, _8192 = 8192 } private TextAsset terrainInfo; // 记录贴图信息的 TextAsset(例如 TerrainInfo.txt) private MergeResolution selectedResolution = MergeResolution._1024; // 自定义行列设置,默认不启用 private bool useCustomGrid = false; private int customGridCount = 8; // 自定义行列数(行数=列数,例如8行8列) // 附加贴图设置(法线、金属度、平滑度、高度) private bool _isMergeNormalMaps; private string _mergeNormalNameSuffix = "_normal"; // 法线贴图后缀 private bool _isMergeMetallicMaps; private string _mergeMetallicMapSuffix = "_metallic"; // 金属度贴图后缀 private bool _isMergeSmoothnessMaps; private string _mergeSmoothnessMapNameSuffix = "_smoothness"; // 平滑度贴图后缀 private bool _isMergeHeightMaps; private string _mergeHeightMapNameSuffix = "_height"; // 高度贴图后缀 // 保存路径 private string saveFolder = Application.dataPath; [MenuItem("Window/Texture Merger")] public static void ShowWindow() { GetWindow("Texture Merger"); } private void OnGUI() { GUILayout.Label("贴图合并设置", EditorStyles.boldLabel); terrainInfo = (TextAsset)EditorGUILayout.ObjectField("Terrain Info (TextAsset)", terrainInfo, typeof(TextAsset), false); selectedResolution = (MergeResolution)EditorGUILayout.EnumPopup("合并后贴图分辨率", selectedResolution); useCustomGrid = EditorGUILayout.Toggle("使用自定义行列", useCustomGrid); if (useCustomGrid) { customGridCount = EditorGUILayout.IntField("网格数量 (行=列)", customGridCount); if (customGridCount < 1) customGridCount = 1; } // 附加贴图设置 GUILayout.Space(10); GUILayout.Label("附加贴图设置", EditorStyles.boldLabel); _isMergeNormalMaps = EditorGUILayout.Toggle("合并法线贴图", _isMergeNormalMaps); if (_isMergeNormalMaps) _mergeNormalNameSuffix = EditorGUILayout.TextField("法线贴图后缀", _mergeNormalNameSuffix); _isMergeMetallicMaps = EditorGUILayout.Toggle("合并金属度贴图", _isMergeMetallicMaps); if (_isMergeMetallicMaps) _mergeMetallicMapSuffix = EditorGUILayout.TextField("金属度贴图后缀", _mergeMetallicMapSuffix); _isMergeSmoothnessMaps = EditorGUILayout.Toggle("合并平滑度贴图", _isMergeSmoothnessMaps); if (_isMergeSmoothnessMaps) _mergeSmoothnessMapNameSuffix = EditorGUILayout.TextField("平滑度贴图后缀", _mergeSmoothnessMapNameSuffix); _isMergeHeightMaps = EditorGUILayout.Toggle("合并高度贴图", _isMergeHeightMaps); if (_isMergeHeightMaps) _mergeHeightMapNameSuffix = EditorGUILayout.TextField("高度贴图后缀", _mergeHeightMapNameSuffix); // 保存路径选择 GUILayout.Space(10); GUILayout.Label("保存路径设置", EditorStyles.boldLabel); if (GUILayout.Button("选择保存路径")) { string folder = EditorUtility.OpenFolderPanel("选择保存路径", Application.dataPath, ""); if (!string.IsNullOrEmpty(folder)) { saveFolder = folder; } } EditorGUILayout.LabelField("当前保存路径:", string.IsNullOrEmpty(saveFolder) ? "未选择" : saveFolder); GUILayout.Space(10); if (GUILayout.Button("合并贴图")) { if (terrainInfo == null) { EditorUtility.DisplayDialog("错误", "请先指定包含贴图信息的 TextAsset", "确定"); return; } MergeTextures(terrainInfo, (int)selectedResolution); } } /// /// 根据 terrainInfo 中的信息加载基础贴图及附加贴图,并分别合并到目标分辨率的贴图上 /// private void MergeTextures(TextAsset terrainInfo, int targetResolution) { // 准备各贴图列表 List baseTextures = new List(); List normalTextures = new List(); List metallicTextures = new List(); List smoothnessTextures = new List(); List heightTextures = new List(); string[] lines = terrainInfo.text.Split(new char[] { '\n' }, System.StringSplitOptions.RemoveEmptyEntries); foreach (string line in lines) { string trimmedLine = line.Trim(); if (string.IsNullOrEmpty(trimmedLine)) continue; // 每行要求格式为:其他信息\t贴图名称 string[] parts = trimmedLine.Split(new char[] { '\t' }, System.StringSplitOptions.RemoveEmptyEntries); if (parts.Length < 2) continue; string texName = Path.GetFileNameWithoutExtension(parts[1]); // 加载基础贴图 string basePath = "Textures/Terrain/" + texName; Texture2D baseTex = Resources.Load(basePath); if (baseTex != null) { baseTextures.Add(baseTex); } else { Debug.LogWarning("在路径 " + basePath + " 找不到基础贴图"); } // 加载法线贴图(必须存在,否则提示错误) if (_isMergeNormalMaps) { string normalPath = "Textures/Terrain/" + texName + _mergeNormalNameSuffix; Texture2D normalTex = Resources.Load(normalPath); if (normalTex != null) { normalTextures.Add(normalTex); } else { EditorUtility.DisplayDialog("错误", "法线贴图缺失: " + normalPath, "确定"); return; } } // 加载金属度贴图 if (_isMergeMetallicMaps) { string metallicPath = "Textures/Terrain/" + texName + _mergeMetallicMapSuffix; Texture2D metallicTex = Resources.Load(metallicPath); if (metallicTex != null) { metallicTextures.Add(metallicTex); } else { Debug.LogWarning("在路径 " + metallicPath + " 找不到金属度贴图"); } } // 加载平滑度贴图 if (_isMergeSmoothnessMaps) { string smoothnessPath = "Textures/Terrain/" + texName + _mergeSmoothnessMapNameSuffix; Texture2D smoothnessTex = Resources.Load(smoothnessPath); if (smoothnessTex != null) { smoothnessTextures.Add(smoothnessTex); } else { Debug.LogWarning("在路径 " + smoothnessPath + " 找不到平滑度贴图"); } } // 加载高度贴图 if (_isMergeHeightMaps) { string heightPath = "Textures/Terrain/" + texName + _mergeHeightMapNameSuffix; Texture2D heightTex = Resources.Load(heightPath); if (heightTex != null) { heightTextures.Add(heightTex); } else { Debug.LogWarning("在路径 " + heightPath + " 找不到高度贴图"); } } } if (baseTextures.Count == 0) { Debug.LogError("没有找到可合并的基础贴图!"); return; } // 合并基础贴图 Texture2D mergedBase = MergeTextureList(baseTextures, targetResolution); SaveTexture(mergedBase, Path.Combine(string.IsNullOrEmpty(saveFolder) ? Application.dataPath : saveFolder, "MergedTexture.png")); // 合并法线贴图 if (_isMergeNormalMaps) { if (normalTextures.Count != baseTextures.Count) { EditorUtility.DisplayDialog("错误", "法线贴图数量与基础贴图数量不匹配!", "确定"); return; } Texture2D mergedNormal = MergeTextureList(normalTextures, targetResolution); SaveTexture(mergedNormal, Path.Combine(string.IsNullOrEmpty(saveFolder) ? Application.dataPath : saveFolder, "MergedTexture" + _mergeNormalNameSuffix + ".png")); } // 合并金属度贴图 if (_isMergeMetallicMaps && metallicTextures.Count > 0) { Texture2D mergedMetallic = MergeTextureList(metallicTextures, targetResolution); SaveTexture(mergedMetallic, Path.Combine(string.IsNullOrEmpty(saveFolder) ? Application.dataPath : saveFolder, "MergedTexture" + _mergeMetallicMapSuffix + ".png")); } // 合并平滑度贴图 if (_isMergeSmoothnessMaps && smoothnessTextures.Count > 0) { Texture2D mergedSmoothness = MergeTextureList(smoothnessTextures, targetResolution); SaveTexture(mergedSmoothness, Path.Combine(string.IsNullOrEmpty(saveFolder) ? Application.dataPath : saveFolder, "MergedTexture" + _mergeSmoothnessMapNameSuffix + ".png")); } // 合并高度贴图 if (_isMergeHeightMaps && heightTextures.Count > 0) { Texture2D mergedHeight = MergeTextureList(heightTextures, targetResolution); SaveTexture(mergedHeight, Path.Combine(string.IsNullOrEmpty(saveFolder) ? Application.dataPath : saveFolder, "MergedTexture" + _mergeHeightMapNameSuffix + ".png")); } } /// /// 将传入的贴图列表按照设定的网格规则合并到一张贴图中 /// private Texture2D MergeTextureList(List textureList, int targetResolution) { int gridCols, gridRows, tileSize, intermediateWidth, intermediateHeight; if (useCustomGrid) { gridCols = customGridCount; gridRows = customGridCount; int maxTiles = gridCols * gridRows; if (textureList.Count > maxTiles) { Debug.LogWarning("贴图数量超过设定的网格数(" + maxTiles + "),将只合并前 " + maxTiles + " 个贴图。"); textureList = textureList.GetRange(0, maxTiles); } tileSize = targetResolution / gridCols; intermediateWidth = targetResolution; intermediateHeight = targetResolution; } else { int count = textureList.Count; gridCols = Mathf.CeilToInt(Mathf.Sqrt(count)); gridRows = Mathf.CeilToInt((float)count / gridCols); tileSize = 512; intermediateWidth = gridCols * tileSize; intermediateHeight = gridRows * tileSize; } // 创建中间纹理,填充黑色背景 Texture2D intermediateTexture = new Texture2D(intermediateWidth, intermediateHeight, TextureFormat.RGBA32, false); Color[] fillColor = new Color[intermediateWidth * intermediateHeight]; for (int i = 0; i < fillColor.Length; i++) fillColor[i] = Color.black; intermediateTexture.SetPixels(fillColor); // 将每张贴图缩放后依次放入中间纹理 for (int i = 0; i < textureList.Count; i++) { Texture2D srcTex = textureList[i]; Texture2D resizedTex = ScaleTexture(srcTex, tileSize, tileSize); int col = i % gridCols; int row = i / gridCols; int startX = col * tileSize; int startY = (gridRows - 1 - row) * tileSize; intermediateTexture.SetPixels(startX, startY, tileSize, tileSize, resizedTex.GetPixels()); } intermediateTexture.Apply(); // 最终贴图:初始化为目标分辨率,并将中间纹理缩放后居中放置 Texture2D finalTexture = new Texture2D(targetResolution, targetResolution, TextureFormat.RGBA32, false); Color[] finalFill = new Color[targetResolution * targetResolution]; for (int i = 0; i < finalFill.Length; i++) finalFill[i] = Color.black; finalTexture.SetPixels(finalFill); float scaleFactor = Mathf.Min((float)targetResolution / intermediateWidth, (float)targetResolution / intermediateHeight); int scaledWidth = Mathf.RoundToInt(intermediateWidth * scaleFactor); int scaledHeight = Mathf.RoundToInt(intermediateHeight * scaleFactor); Texture2D scaledIntermediate = ScaleTexture(intermediateTexture, scaledWidth, scaledHeight); int offsetX = (targetResolution - scaledWidth) / 2; int offsetY = (targetResolution - scaledHeight) / 2; finalTexture.SetPixels(offsetX, offsetY, scaledWidth, scaledHeight, scaledIntermediate.GetPixels()); finalTexture.Apply(); return finalTexture; } /// /// 使用 RenderTexture 和 Graphics.Blit 进行高性能的贴图缩放 /// 1. 结果贴图统一使用 RGBA32 格式 /// 2. Blit 后调用 GL.Flush() 确保渲染命令执行完毕 /// 3. 保存并恢复之前的 RenderTexture.active /// private Texture2D ScaleTexture(Texture2D source, int targetWidth, int targetHeight) { RenderTexture rt = RenderTexture.GetTemporary(targetWidth, targetHeight); rt.filterMode = FilterMode.Bilinear; RenderTexture previous = RenderTexture.active; RenderTexture.active = rt; Graphics.Blit(source, rt); GL.Flush(); Texture2D result = new Texture2D(targetWidth, targetHeight, TextureFormat.RGBA32, false); result.ReadPixels(new Rect(0, 0, targetWidth, targetHeight), 0, 0); result.Apply(); RenderTexture.active = previous; RenderTexture.ReleaseTemporary(rt); return result; } /// /// 将生成的贴图保存为 PNG 文件,并刷新 AssetDatabase /// private void SaveTexture(Texture2D texture, string path) { byte[] pngData = texture.EncodeToPNG(); File.WriteAllBytes(path, pngData); Debug.Log("合并贴图已保存至: " + path); AssetDatabase.Refresh(); } }