背景:ETC1图片格式的罪孽,不支持Alpha通道。于是程序员们将一些气力浪费在Alpha通道的处理上。 为了能使用ETC1,同时某些透明效果必须有Alpha通道,一般的处理方式是将RGB和Alpha分为两张图片分别储存。 只存Alpha通道的图片及RGB都为要存的Alpha值,因为熵比较小,图片尺寸也可以相应减小一些。
要做的工作:
1. 将带有Alpha通道的图片,另存为两张图片,一张只存RGB信息,另一张只存Alpha信息。建议保持为图片原目录,名称加后缀“_RGB”, "_Alpha"。
2. 带有Alpha通道的图片,所用的Shader要更新为支持RGB和Alpha信息分别从两张不同图片读取的shader。这个功能,因为不能的Material的Shader会很不一样,因此,不用程序来强硬指定了。但程序起码需要给出提示,工程中哪些Material用到了哪些带有Alpha通道的图片。
不罗嗦了,直接上代码。
using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEditor; using System.IO; using System.Reflection; public class MaterialTextureForETC1{ public static float sizeScale = 0.5f; //the size decrease scale for alphaTexture public static Dictionary<string, bool> texturesAlphaDic = new Dictionary<string, bool>(); [MenuItem("EffortForETC1/Seperate RGB and Alpha Channel for All Textures")] static void SeperateAllTexturesRGBandAlphaChannel() { string[] paths = Directory.GetFiles(Application.dataPath, "*.*", SearchOption.AllDirectories); foreach (string path in paths) { if (!string.IsNullOrEmpty(path) && IsTextureFile(path)) //full name { SeperateRGBAandlphaChannel(path); } } } [MenuItem("EffortForETC1/Show Materials Having Textures with Alpha Chanel")] static void ShowMaterialsHavingTextureswithAlphaChanel() { CalculateTexturesAlphaChannelDic(); string[] matpaths = Directory.GetFiles(Application.dataPath, "*.mat", SearchOption.AllDirectories); foreach (string matpath in matpaths) { string propermatpath = GetRelativeAssetPath(matpath); Material mat = (Material)Resources.LoadAssetAtPath(propermatpath, typeof(Material)); if (mat) { string[] alphatexpaths = GetMaterialTexturesHavingAlphaChannel(mat); if (alphatexpaths.Length == 0) { continue; } Debug.Log("Material having texture(s) with Alpha channel : " + propermatpath); foreach (string alphatexpath in alphatexpaths) { Debug.Log(alphatexpath + " in " + propermatpath); } } else { Debug.LogError("Load material failed : " + matpath); } } Debug.Log("Finish!"); } #region inspect material static string[] GetMaterialTexturesHavingAlphaChannel(Material _mat) { List<string> alphatexpaths = new List<string>(); string[] texpaths = GetMaterialTexturePaths(_mat); foreach (string texpath in texpaths) { if (texturesAlphaDic[texpath]) { alphatexpaths.Add(texpath); } } return alphatexpaths.ToArray(); } static string[] GetMaterialTexturePaths(Material _mat) { List<string> results = new List<string>(); Object[] roots = new Object[] { _mat }; Object[] dependObjs = EditorUtility.CollectDependencies(roots); foreach (Object dependObj in dependObjs) { if (dependObj.GetType() == typeof(Texture2D)) { string texpath = AssetDatabase.GetAssetPath(dependObj.GetInstanceID()); results.Add(texpath); } } return results.ToArray(); } #endregion static void CalculateTexturesAlphaChannelDic() { string[] paths = Directory.GetFiles(Application.dataPath, "*.*", SearchOption.AllDirectories); foreach (string path in paths) { if (!string.IsNullOrEmpty(path) && IsTextureFile(path)) //full name { string assetRelativePath = GetRelativeAssetPath(path); SetTextureReadable(assetRelativePath); Texture2D sourcetex = Resources.LoadAssetAtPath(assetRelativePath, typeof(Texture2D)) as Texture2D; if (!sourcetex) //make sure the file is really Texture2D which can be loaded as Texture2D. { continue; } if (HasAlphaChannel(sourcetex)) { AddValueToDic(assetRelativePath, true); } else { AddValueToDic(assetRelativePath, false); } } } } static void AddValueToDic(string _key, bool _val) { if (texturesAlphaDic.ContainsKey(_key)) { texturesAlphaDic[_key] = _val; } else { texturesAlphaDic.Add(_key, _val); } } #region process texture static void SeperateRGBAandlphaChannel(string _texPath) { string assetRelativePath = GetRelativeAssetPath(_texPath); SetTextureReadable(assetRelativePath); Texture2D sourcetex = Resources.LoadAssetAtPath(assetRelativePath, typeof(Texture2D)) as Texture2D; //not just the textures under Resources file if (!sourcetex) { Debug.Log("Load Texture Failed : " + assetRelativePath); return; } if (!HasAlphaChannel(sourcetex)) { Debug.Log("Texture does not have Alpha channel : " + assetRelativePath); return; } Texture2D rgbTex = new Texture2D(sourcetex.width, sourcetex.height, TextureFormat.RGB24, true); Texture2D alphaTex = new Texture2D((int)(sourcetex.width * sizeScale), (int)(sourcetex.height * sizeScale), TextureFormat.RGB24, true); for (int i = 0; i < sourcetex.width; ++i) for (int j = 0; j < sourcetex.height; ++j) { Color color = sourcetex.GetPixel(i, j); Color rgbColor = color; Color alphaColor = color; alphaColor.r = color.a; alphaColor.g = color.a; alphaColor.b = color.a; rgbTex.SetPixel(i, j, rgbColor); alphaTex.SetPixel((int)(i * sizeScale), (int)(j * sizeScale), alphaColor); } rgbTex.Apply(); alphaTex.Apply(); byte[] bytes = rgbTex.EncodeToPNG(); File.WriteAllBytes(GetRGBTexPath(_texPath), bytes); bytes = alphaTex.EncodeToPNG(); File.WriteAllBytes(GetAlphaTexPath(_texPath), bytes); Debug.Log("Succeed to seperate RGB and Alpha channel for texture : " + assetRelativePath); } static bool HasAlphaChannel(Texture2D _tex) { for (int i = 0; i < _tex.width; ++i) for (int j = 0; j < _tex.height; ++j) { Color color = _tex.GetPixel(i, j); float alpha = color.a; if (alpha < 1.0f - 0.001f) { return true; } } return false; } static void SetTextureReadable(string _relativeAssetPath) { string postfix = GetFilePostfix(_relativeAssetPath); if (postfix == ".dds") // no need to set .dds file. Using TextureImporter to .dds file would get casting type error. { return; } TextureImporter ti = (TextureImporter)TextureImporter.GetAtPath(_relativeAssetPath); ti.isReadable = true; AssetDatabase.ImportAsset(_relativeAssetPath); } #endregion #region string or path helper static bool IsTextureFile(string _path) { string path = _path.ToLower(); return path.EndsWith(".psd") || path.EndsWith(".tga") || path.EndsWith(".png") || path.EndsWith(".jpg") || path.EndsWith(".dds") || path.EndsWith(".bmp") || path.EndsWith(".tif") || path.EndsWith(".gif"); } static string GetRGBTexPath(string _texPath) { return GetTexPath(_texPath, "_RGB."); } static string GetAlphaTexPath(string _texPath) { return GetTexPath(_texPath, "_Alpha."); } static string GetTexPath(string _texPath, string _texRole) { string result = _texPath.Replace(".", _texRole); string postfix = GetFilePostfix(_texPath); return result.Replace(postfix, ".png"); } static string GetRelativeAssetPath(string _fullPath) { _fullPath = GetRightFormatPath(_fullPath); int idx = _fullPath.IndexOf("Assets"); string assetRelativePath = _fullPath.Substring(idx); return assetRelativePath; } static string GetRightFormatPath(string _path) { return _path.Replace("\\", "/"); } static string GetFilePostfix(string _filepath) //including '.' eg ".tga", ".dds" { string postfix = ""; int idx = _filepath.LastIndexOf('.'); if (idx > 0 && idx < _filepath.Length) postfix = _filepath.Substring(idx, _filepath.Length - idx); return postfix; } #endregion }
上面的代码中,图片的处理,用的是已经整合进Unity的有限的图片处理功能,分离RGB和Alpha通道的图片都被存为了.png格式。其实可以将C#的System.Drawing.dll导入到工程里,用C#原生的Bitmap类来处理图片。
.dds格式图片的处理比较特殊。一般用Texture2D.GetPixel()等函数处理Texture2D时,需要设置Texture的Import属性里的Readable,但.dds格式的图片不用处理。同时,Unity只支持Int格式的dds, float32格式的.dds不支持。
时间: 2024-11-09 22:47:37