美文网首页
NGUI打图集工具Alpha通道分离

NGUI打图集工具Alpha通道分离

作者: 吴少年 | 来源:发表于2019-05-07 17:32 被阅读0次

    工具设计目的

    NGUI中图集默认使用"Unlit/Transparent Colored" Shader来创建材质。这样的话需要一张RGBA32的带透明通道的贴图,一张515x512的图占用空间1M,加载到内存后变成2M。在手机内存还是比较宝贵的时代这个是不大能接受的。一般项目的做法是分隔成两张图一张color图包含RGB通道,一张alpha图包含A通道。通过shader来组合长RGBA的图,来实现接近于RGBA的效果。通常Android使用ETC格式,IOS使用PVRTC格式。一张ETC格式的512x512的图占用128kb,两张一起256kb。这样相对于RGBA32格式的只占用其四分之一的空间。

    NGUI自带的图集工具并不支持打ETC通道分离。通常一般做法采用第三方工具TexturePacker来做这个,但是使用第三方工具来做这个也会比较麻烦。所以通过修改NGUI打图集工具来实现这个通道分离。

    图集增加通道分离

    通道分离的做法是生成一张完整RGBA32的图片,然后读取RGBA分别生成两张和RGBA32一样大小的RGB图片和Alpha图片。最后替换shader,生成两张图片合成的材质 。代码如下:

    using UnityEngine;
    using System.Collections;
    using UnityEditor;
    using System.Collections.Generic;
    using System.IO;
    using com.geargames.common.utils;
    
    public class UIAtlasChangeShaderTool : EditorWindow
    {
        private bool _mSearched;
        private Object[] mObjects;
        private Vector2 mScroll = Vector2.zero;
        public static bool useAlpha = true;
        const string defShader = "Unlit/Transparent Colored";
        const string newShader = "这里填合成两张图的shader名字";
    
        private readonly string[] atlasPaths = { "这里填图集所在路径"};
    
        [MenuItem("Tool/Open UIAtlas Shader Change Window")]
        public static void OpenWindow()
        {
            UIAtlasChangeShaderTool window = (UIAtlasChangeShaderTool)EditorWindow.GetWindow(typeof(UIAtlasChangeShaderTool));
            window.Show();
        }
        void OnGUI()
        {
            if (mObjects != null && mObjects.Length != 0)
            {
                mScroll = GUILayout.BeginScrollView(mScroll);
                foreach (Object o in mObjects)
                {
                    DrawUIAtalsObject(o);
                }
                GUILayout.EndScrollView();
            }
            GUILayout.Space(6f);
            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            bool search = GUILayout.Button("Show All", "LargeButton", GUILayout.Width(120f));
            bool Normal = GUILayout.Button("Normal All", "LargeButton", GUILayout.Width(120f));
            bool Etc = GUILayout.Button("Etc All", "LargeButton", GUILayout.Width(120f));
            bool CreateTexture = GUILayout.Button("CreateTexture All", "LargeButton", GUILayout.Width(200f));
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();
            if (search) Search(typeof(UIAtlas));
    
            if (mObjects == null || mObjects.Length == 0) return;
            if (Normal) foreach (Object o in mObjects) _ChangeShaderToNormal(o as UIAtlas);
            if (Etc) foreach (Object o in mObjects) _ChangeShaderToEtc(o as UIAtlas);
            if (CreateTexture)foreach (Object o in mObjects) SeperateRGBAandlphaChannel((o as UIAtlas).spriteMaterial.mainTexture as Texture2D);
        }
    
        private void DrawUIAtalsObject(Object obj)
        {
            if (obj == null) return;
            Component comp = obj as Component;
            UIAtlas at = obj as UIAtlas;
            GUILayout.BeginHorizontal();
            {
                string path = AssetDatabase.GetAssetPath(obj);
    
                if (string.IsNullOrEmpty(path))
                {
                    path = "[Embedded]";
                    GUI.contentColor = new Color(0.7f, 0.7f, 0.7f);
                }
                else if (comp != null && EditorUtility.IsPersistent(comp.gameObject))
                    GUI.contentColor = new Color(0.6f, 0.8f, 1f);
    
                GUILayout.Label(obj.name, "TextArea", GUILayout.Width(160f), GUILayout.Height(20f));
                GUILayout.Label(path.Replace("Assets/", ""), "TextArea", GUILayout.Width(300f), GUILayout.Height(20f));
                GUILayout.Label(at.spriteMaterial.shader.name, "TextArea", GUILayout.Width(200f), GUILayout.Height(20f));
                GUI.contentColor = Color.white;
                if (GUILayout.Button("Normal", "ButtonLeft", GUILayout.Width(60f), GUILayout.Height(16f)))
                {
                    _ChangeShaderToNormal(at);
                    AssetDatabase.Refresh();
                }
    
                if (GUILayout.Button("Etc", "ButtonLeft", GUILayout.Width(60f), GUILayout.Height(16f)))
                {
                    _ChangeShaderToEtc(at);
                    AssetDatabase.Refresh();
                }
                
                if (GUILayout.Button("CreateTexture", "ButtonLeft", GUILayout.Width(100f), GUILayout.Height(16f)))
                {
                   string assetPath =  AssetDatabase.GetAssetPath(at).Replace(".prefab",".png")  ;
                    Texture2D texture = AssetDatabase.LoadAssetAtPath(assetPath,typeof(Texture2D)) as Texture2D;
                    SeperateRGBAandlphaChannel(at.texture as Texture2D);
                    AssetDatabase.Refresh();
                }
            }
            GUILayout.EndHorizontal();
    
        }
        protected void Search(System.Type mType)
        {
            _mSearched = true;
            List<string> pathList = new List<string>();
            _GetAllAtlasPath(pathList);
            bool isComponent = mType.IsSubclassOf(typeof(Component));
            List<Object> list = new List<Object>();
    
            if (mObjects != null)
            {
                for (int i = 0; i < mObjects.Length; ++i)
                    if (mObjects[i] != null)
                        list.Add(mObjects[i]);
            }
    
            string path = "";
    
            string[] paths = pathList.ToArray();
            for (int i = 0; i < paths.Length; ++i)
            {
                path = paths[i];
    
                EditorUtility.DisplayProgressBar("Loading", "Searching assets, please wait...", (float)i / paths.Length);
                Object obj = AssetDatabase.LoadMainAssetAtPath(path);
                if (obj == null || list.Contains(obj)) continue;
    
                if (!isComponent)
                {
                    System.Type t = obj.GetType();
                    if (t == mType || t.IsSubclassOf(mType) && !list.Contains(obj))
                        list.Add(obj);
                }
                else if (PrefabUtility.GetPrefabType(obj) == PrefabType.Prefab)
                {
                    Object t = (obj as GameObject).GetComponent(mType);
                    if (t != null && !list.Contains(t)) list.Add(t);
                }
            }
            list.Sort(delegate (Object a, Object b) { return a.name.CompareTo(b.name); });
            mObjects = list.ToArray();
    
            EditorUtility.ClearProgressBar();
        }
        private void _GetAllAtlasPath(List<string> pathList)
        {
            for (int i = 0; i < atlasPaths.Length; i++)
            {
                FileUtils.GetAllFiles(Application.dataPath + atlasPaths[i], pathList);
            }
    
            for (int i = 0; i < pathList.Count; i++)
            {
                pathList[i] = pathList[i].Replace("\\", "/").Replace(Application.dataPath, "Assets");
            }
        }
    
        static string c_flg = "_C";
        static string a_flg = "_A";
        static string exp_flg = ".png";
        static private void _ChangeShaderToNormal(UIAtlas obj)
        {
            if (obj.spriteMaterial.shader.name != defShader)
            {
                obj.spriteMaterial.shader = Shader.Find(defShader);
                string path = AssetDatabase.GetAssetPath(obj.spriteMaterial.mainTexture);
                string p = path.Substring(0, path.IndexOf(c_flg + "."))+ ".png";
                obj.spriteMaterial.mainTexture = AssetDatabase.LoadAssetAtPath(p , typeof(Texture2D)) as Texture2D;
                AssetDatabase.SaveAssets();
            }
        }
        static private void _ChangeShaderToEtc(UIAtlas obj)
        {
            if (obj.spriteMaterial.shader.name != newShader)
            {
                obj.spriteMaterial.shader = Shader.Find(newShader);
                string path = AssetDatabase.GetAssetPath(obj.spriteMaterial.mainTexture);
                string p = path.Substring(0, path.IndexOf("."));
                string cpath = p + c_flg + exp_flg;
                string apath = p + a_flg + exp_flg;
                obj.spriteMaterial.mainTexture = AssetDatabase.LoadAssetAtPath(cpath, typeof(Texture2D)) as Texture2D;
                obj.spriteMaterial.SetTexture("_MaskTex", AssetDatabase.LoadAssetAtPath(apath, typeof(Texture2D)) as Texture2D);
                Debug.Log("path = " + cpath + "  " + apath);
                AssetDatabase.SaveAssets();
            }
        }
        static void SeperateRGBAandlphaChannel(Texture2D sourcetex)
        {
            string path = AssetDatabase.GetAssetPath(sourcetex);
            AssetDatabase.ImportAsset(path);
            string p = path.Substring(0, path.IndexOf("."));
            string cpath = p + c_flg + exp_flg;
            string apath = p + a_flg + exp_flg;
            AssetDatabase.DeleteAsset(cpath);
            AssetDatabase.DeleteAsset(apath);
            TextureImporter ti = AssetImporter.GetAtPath(path) as TextureImporter;
            MakeTextureReadable(ti, path, false);
            Color[] sc = sourcetex.GetPixels();
            int sw = sourcetex.width;
            int sh = sourcetex.height;
            Texture2D rgbTex = new Texture2D(sw, sh, TextureFormat.RGB24, false);
            Texture2D alphaTex = new Texture2D(sw, sh, TextureFormat.RGB24, false);
            Color[] ac = new Color[sw * sh];
            for (int i = 0; i <sw; i++)
            {
                for (int j = 0; j < sh; j++)
                {
                    int ind = j * sw + i;
                    Color color = sc[ind];
                    color.r = color.a;
                    color.g = color.a;
                    color.b = color.a;
                    ac[ind] = color;
                }
            }
            rgbTex.SetPixels(sc);
            rgbTex.Apply();
            alphaTex.SetPixels(ac);
            alphaTex.Apply();
            byte[] bytes = rgbTex.EncodeToPNG();
            File.WriteAllBytes(cpath, bytes);
            bytes = alphaTex.EncodeToPNG();
            File.WriteAllBytes(apath, bytes);
    
            AssetDatabase.ImportAsset(cpath);
            AssetDatabase.ImportAsset(apath);
            TextureImporter tic = AssetImporter.GetAtPath(cpath) as TextureImporter;
            TextureImporter tia = AssetImporter.GetAtPath(apath) as TextureImporter;
            SetTextureFormat(tic, tia);
            MakeTextureAnAtlas(ti, path, false, true);
            MakeTextureAnAtlas(tic, cpath, false, false);
            MakeTextureAnAtlas(tia, apath, false, true);
        }
        static public bool MakeTextureReadable(TextureImporter ti, string path, bool force)
        {
            TextureImporterSettings settings = new TextureImporterSettings();
            ti.ReadTextureSettings(settings);
    
            if (force || !settings.readable || settings.npotScale != TextureImporterNPOTScale.None || settings.alphaIsTransparency)
            {
                settings.readable = true;
                if (NGUISettings.trueColorAtlas) settings.textureFormat = TextureImporterFormat.AutomaticTruecolor;
                settings.npotScale = TextureImporterNPOTScale.None;
                settings.alphaIsTransparency = false;
                ti.SetTextureSettings(settings);
                AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
            }
            return true;
        }
        static bool MakeTextureAnAtlas(TextureImporter ti, string path, bool force, bool alphaTransparency)
        {
            TextureImporterSettings settings = new TextureImporterSettings();
            ti.ReadTextureSettings(settings);
    
            if (force ||
                settings.readable ||
                settings.maxTextureSize < 4096 ||
                settings.wrapMode != TextureWrapMode.Clamp ||
                settings.npotScale != TextureImporterNPOTScale.ToNearest)
            {
                settings.readable = false;
                settings.maxTextureSize = 4096;
                settings.wrapMode = TextureWrapMode.Clamp;
                settings.npotScale = TextureImporterNPOTScale.ToNearest;
    
                if (NGUISettings.trueColorAtlas)
                {
                    settings.textureFormat = TextureImporterFormat.ARGB32;
                    settings.filterMode = FilterMode.Trilinear;
                }
    
                settings.aniso = 4;
                settings.alphaIsTransparency = alphaTransparency;
                ti.SetTextureSettings(settings);
                AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
            }
            return true;
        }
        static public void SetTextureFormat(TextureImporter tic, TextureImporter tia)
        {
            tic.alphaIsTransparency = false;
            tic.isReadable = tia.isReadable = false;
            tic.mipmapEnabled = tia.mipmapEnabled = false;
            // TextureImporterPlatformSettings textureImporterPlatformSettings = tic.
            // UnityEditor.TextureImporter.SetPlatformTextureSettings();
            
            TextureImporterPlatformSettings tips = new TextureImporterPlatformSettings();
            tips.name = "Android";
            tips.overridden = true;
            tips.maxTextureSize = 2048;
            tips.format = TextureImporterFormat.ETC_RGB4;
            tips.textureCompression = (int)UnityEditor.TextureCompressionQuality.Fast;
    
            tic.SetPlatformTextureSettings(tips);
            tia.SetPlatformTextureSettings(tips);
            tips.overridden = true;
            tips.name = "iPhone";
            tips.format = TextureImporterFormat.PVRTC_RGB4;
            tic.SetPlatformTextureSettings(tips);
            tia.SetPlatformTextureSettings(tips);
    #if UNITY_ANDROID || UNITY_STANDALONE
            tips.name = "Standalone";
            tips.format = TextureImporterFormat.ETC_RGB4;
            tips.overridden = true;
            tic.SetPlatformTextureSettings(tips);
            tia.SetPlatformTextureSettings(tips);
            // tic.SetPlatformTextureSettings("Standalone", 1024, TextureImporterFormat.ETC_RGB4, (int)TextureCompressionQuality.Fast);
            // tia.SetPlatformTextureSettings("Standalone", 1024, TextureImporterFormat.ETC_RGB4, (int)TextureCompressionQuality.Fast);
            // tic.textureFormat = TextureImporterFormat.ETC_RGB4;
            // tia.textureFormat = TextureImporterFormat.ETC_RGB4;
    #else
            tips.name = "Standalone";
            tips.format = TextureImporterFormat.PVRTC_RGB4;
            tips.overridden = true;
            tic.SetPlatformTextureSettings(tips);
            tia.SetPlatformTextureSettings(tips);
            // tic.SetPlatformTextureSettings("Standalone", 1024, TextureImporterFormat.PVRTC_RGB4, (int)TextureCompressionQuality.Fast);
            // tia.SetPlatformTextureSettings("Standalone", 1024, TextureImporterFormat.PVRTC_RGB4, (int)TextureCompressionQuality.Fast);
            // tic.textureFormat = TextureImporterFormat.PVRTC_RGB4;
            // tia.textureFormat = TextureImporterFormat.PVRTC_RGB4;
    #endif
        }
    
        public static bool flg = false;
        static public void OnInspectorGUI(UIAtlas mLastAtlas)
        {
            GUILayout.BeginHorizontal();
            UIAtlasChangeShaderTool.flg = EditorGUILayout.Toggle("spaceAlpha", UIAtlasChangeShaderTool.flg, GUILayout.Width(100f));
            GUILayout.Label("分隔color,Alpha两通道");
            GUILayout.EndHorizontal();
        }
        static public void updateAtlas(UIAtlas mLastAtlas)
        {
            updateAtlas(mLastAtlas, UIAtlasChangeShaderTool.flg);
        }
        static public void updateAtlas(UIAtlas mLastAtlas, bool flg)
        {
            if (flg)
            {
                SeperateRGBAandlphaChannel(mLastAtlas.texture as Texture2D);
                _ChangeShaderToEtc(mLastAtlas);
            }
            else
            {
                _ChangeShaderToNormal(mLastAtlas);
            }
            AssetDatabase.Refresh();
        }
    }
    

    注意,我这用的是Unity2018.3,比较早期的版本在图集格式设置那块需要修改 。就是SetPlatformTextureSettings这个方法。
    NGUI中打图集的工具写在UIAtlasMaker.cs里面。我们需要在系统打图集的选项里面增加一个选择打分离通道的。如下:


    image.png

    UIAtlasMaker.cs需要增加代码位置为:

        void OnEnable () { instance = this;
                    //默认勾选上
            UIAtlasChangeShaderTool.flg = true;
        }
    
    void OnGUI ()
        {
    //省略之前代码。在布局的最后添加这个。
            UIAtlasChangeShaderTool.OnInspectorGUI(NGUISettings.atlas);
            NGUIEditorTools.EndContents();
    
                    if (delete)
                    {
                        List<SpriteEntry> sprites = new List<SpriteEntry>();
                        ExtractSprites(NGUISettings.atlas, sprites);
    
                        for (int i = sprites.Count; i > 0; )
                        {
                            SpriteEntry ent = sprites[--i];
                            if (mDelNames.Contains(ent.name))
                                sprites.RemoveAt(i);
                        }
                        UpdateAtlas(NGUISettings.atlas, sprites);
                        mDelNames.Clear();
                        NGUIEditorTools.RepaintSprites();
                    }
                    else if (update){
                         //更新的位置还原默认图集,用来生成RGBA32
                        UIAtlasChangeShaderTool.updateAtlas(NGUISettings.atlas, false);
                        UpdateAtlas(textures, true);
                    } 
                    else if (replace) UpdateAtlas(textures, false);
    
                    if (NGUISettings.atlas != null && !string.IsNullOrEmpty(selection))
                    {
                        NGUIEditorTools.SelectSprite(selection);
                    }
                    else if (NGUISettings.autoUpgradeSprites && (update || replace))
                    {
                        NGUIEditorTools.UpgradeTexturesToSprites(NGUISettings.atlas);
                        NGUIEditorTools.RepaintSprites();
                      //这里生成两个图集
                        UIAtlasChangeShaderTool.updateAtlas(NGUISettings.atlas);
                    }
    }
    

    相关文章

      网友评论

          本文标题:NGUI打图集工具Alpha通道分离

          本文链接:https://www.haomeiwen.com/subject/pqcroqtx.html