Unity3D 优化NGUI纹理

Posted MrBlue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity3D 优化NGUI纹理相关的知识,希望对你有一定的参考价值。

原理就是将一张rgba 32的分成两张纹理:一张平台压缩不带alpha和一张为原图1/4大小的压缩图存储alpha信息(用r分量当alpha值),然后修改原材质的Shader传入这两张纹理。

 

代码如下(这个是比较针对我们项目的,你可以自己手动改下)

注意:NGUI的UIPannel裁剪是需要自己手动写不同裁剪层级的Shader,这个不要忘记写

using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System.IO;

public class OptimizeAtlasMaterialTextureTool
{
    public static float sizeScale = 0.5f;
    public static string TransparentColoredShaderName = "Unlit/Transparent Colored(RGBA)";
    public static string TransparentGreyedShaderName = "Unlit/Transparent Grey(RGBA)";
    public static readonly List<string>[] NeedProcessMaterialPath =
        {
            //new List<string>{"Assets/ArtAsset/UI/Login/Loading.mat"},
             
               //new List<string>{"Assets/ArtAsset/UI/zhangjing/NewTools.mat","Assets/ArtAsset/UI/zhangjing/NewToolsGrey.mat"},
               new List<string>{"Assets/ArtAsset/UI/zhangjing/NewTools2.mat","Assets/ArtAsset/UI/zhangjing/NewTools2Grey.mat"},
               new List<string>{"Assets/ArtAsset/UI/zhangjing/NewTools3.mat"},
            new List<string>{"Assets/ArtAsset/UI/zhangjing/NewTools4.mat"},
            new List<string>{"Assets/ArtAsset/UI/zhangjing/NewTools5.mat"},
            new List<string>{"Assets/ArtAsset/UI/zhangjing/NewTools6.mat","Assets/ArtAsset/UI/zhangjing/NewTools6Grey.mat"},
            new List<string>{"Assets/ArtAsset/UI/zhangjing/NewTools7.mat"},
            new List<string>{"Assets/ArtAsset/UI/zhangjing/NewTools8.mat"},

            new List<string>{"Assets/ArtAsset/UI/zhangjing/NewTools9.mat"},
            new List<string>{"Assets/ArtAsset/UI/zhangjing/NewTools10.mat"},
            new List<string>{"Assets/ArtAsset/UI/zhangjing/NewTools12.mat"},
            new List<string>{"Assets/ArtAsset/UI/zhangjing/NewTools13.mat"},

            new List<string>{"Assets/ArtAsset/UI/zhangjing/Fuben.mat","Assets/ArtAsset/UI/zhangjing/FubenGrey.mat"},
            new List<string>{"Assets/ArtAsset/UI/cml_maya/MieShi.mat"},
            new List<string>{"Assets/ArtAsset/UI/zhangjing/Huodong.mat","Assets/ArtAsset/UI/zhangjing/HuodongGrey.mat"},
            
            //new List<string>{"Assets/ArtAsset/UI/SkillIcon/SkillIcon.mat","Assets/ArtAsset/UI/SkillIcon/SkillIconGrey.mat"},

        };

    public static void OptimizeAtlasMaterial(List<string>[] msPath)
    {
        try
        {
            int i = 0;
            foreach (var s in msPath)
            {
                var ms = new Material[s.Count];
                for (int j=0; j<s.Count; j++)
                {
                    ms[j] = AssetDatabase.LoadAssetAtPath(s[j], typeof(Material)) as Material;
                }
    
                ProcessUIMaterial(ms);
                
                EditorUtility.DisplayProgressBar("OptimizeAtlasMaterialTextureTool", msPath[i][0], i * 1.0f / msPath.Length);
                i++;
            }

        }
        finally
        {
            EditorUtility.ClearProgressBar();
                    
        }
    }

    [MenuItem("Assets/Seperate Material‘s Texture RGB and Alpha")]
    static void OptimizeAtlasMaterialTexture()
    {

        var os = Selection.objects;
        foreach (var o in os)
        {
            var m = o as Material;
            if (null != m)
            {
                ProcessUIMaterial(new []{m});
            }
        }
    }

    [MenuItem("Tools/Atlas Tool/Seperate Material‘s Texture RGB and Alpha")]
    public static void ToolOptimizeAtlasMaterialTexture()
    {
        OptimizeAtlasMaterial(NeedProcessMaterialPath);
    }

    public static void ProcessUIMaterial(Material[] ms)
    {
        if (null == ms || ms.Length<=0)
        {
            return;
        }

        var s = Shader.Find(TransparentColoredShaderName);
        var sg = Shader.Find(TransparentGreyedShaderName);

        var m = ms[0];
        if (null == m || null == m.mainTexture)
        {
            return;
        }

        var str = AssetDatabase.GetAssetPath(m.mainTexture.GetInstanceID());
        string rgbPath = "";
        string aPath = "";
        if (!SeperateRGBAandlphaChannel(str, out rgbPath, out aPath))
        {
            return;
        }


        Texture2D t = AssetDatabase.LoadAssetAtPath(rgbPath, typeof(Texture2D)) as Texture2D;
        Texture2D t2 = AssetDatabase.LoadAssetAtPath(aPath, typeof(Texture2D)) as Texture2D;

        foreach (var material in ms)
        {
            if (null == material)
            {
                continue;
            }
            if (material.name.ToLower().Contains("grey"))
            {
                material.shader = sg;
            }
            else
            {
                material.shader = s;
            }
            material.SetTexture("_MainTex", t);
            material.SetTexture("_AlphaTex", t2);
        }

        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();

        Debug.Log("process " + str);
    }

    #region process texture

    static bool SeperateRGBAandlphaChannel(string _texPath,out string rgbPath,out string aPath)
    {
        rgbPath = "";
        aPath = "";
        string assetRelativePath = GetRelativeAssetPath(_texPath);
        SetTextureReadable(assetRelativePath);
        Texture2D sourcetex = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(Texture2D)) as Texture2D;  //not just the textures under Resources file  
        if (!sourcetex)
        {
            Debug.Log("Load Texture Failed : " + assetRelativePath);
            return false;
        }
//         if (!HasAlphaChannel(sourcetex))
//         {
//             Debug.Log("Texture does not have Alpha channel : " + assetRelativePath);
//             return false;
//         }

        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);
             }

//         { 
//             var sourcetexPixels = sourcetex.GetPixels();
//             var rgbColors = new Color[sourcetexPixels.Length];
//             var alphaColors = new Color[sourcetexPixels.Length];
//             for (int i = 0; i < sourcetexPixels.Length; i++)
//             {
//                 var color = sourcetexPixels[i];
//                 rgbColors[i] = color;
//                 //alphaColors[(int)(i * sizeScale)].r = color.a;
//                 alphaColors[(int)(i * sizeScale)].r = color.a;
//                 alphaColors[(int)(i * sizeScale)].g = color.a;
//                 alphaColors[(int)(i * sizeScale)].b = color.a;
//                 alphaColors[(int)(i * sizeScale)].a = color.a;
//             }
//             rgbTex.SetPixels(rgbColors);
//             alphaTex.SetPixels(alphaColors);
//         }

        rgbTex.Apply();
        alphaTex.Apply();

        rgbPath = GetRGBTexPath(_texPath);
        aPath = GetAlphaTexPath(_texPath);

        byte[] bytes = rgbTex.EncodeToPNG();
        File.WriteAllBytes(rgbPath, bytes);
        bytes = alphaTex.EncodeToPNG();
        File.WriteAllBytes(aPath, bytes);
        Debug.Log("Succeed to seperate RGB and Alpha channel for texture : " + assetRelativePath);
        AssetDatabase.Refresh();

        TexturePostProcess(rgbPath, Math.Max(sourcetex.width, sourcetex.height));
        TexturePostProcess(aPath, (int)(Math.Max(sourcetex.width, sourcetex.height)*sizeScale));

        AssetDatabase.Refresh();

        return true;
    }

    static bool HasAlphaChannel(Texture2D _tex)
    {
        var texPixels = _tex.GetPixels32(0);
        var count = texPixels.Length;

        for (int i = 0; i < count; i++)
        {
            if (texPixels[i].a < 1.0f -0.001f)
            {
                return true;
            }
        }

        return false;

//         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);

        bool change = false;

        if (ti.isReadable != true)
        {
            ti.isReadable = true;
            change = true;
        }
        
        var platform = "";
#if UNITY_STANDALONE
        platform = "Standalone";
#elif UNITY_android
        platform = "Android";
#elif UNITY_IPHONE|| UNITY_ios
        platform = "iPhone";
#endif
        var size = 0;
        TextureImporterFormat tf;
        ti.GetPlatformTextureSettings(platform, out size, out tf);
        if (TextureImporterFormat.AutomaticTruecolor!=tf  && 
            tf != TextureImporterFormat.RGBA32 &&
            tf != TextureImporterFormat.ARGB32)
        {
            ti.SetPlatformTextureSettings(platform, size, TextureImporterFormat.RGBA32);
            change = true;
        }

        if (change)
        {
            AssetDatabase.ImportAsset(_relativeAssetPath);    
        }
        
    }

    static void TexturePostProcess(string _relativeAssetPath,int maxsize = 2048)
    {
        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.textureType = TextureImporterType.Advanced;
        ti.isReadable = false;
        ti.mipmapEnabled = false;


        ti.SetPlatformTextureSettings("Standalone", maxsize, TextureImporterFormat.RGB24);
        ti.SetPlatformTextureSettings("Android", maxsize, TextureImporterFormat.ETC_RGB4);
        ti.SetPlatformTextureSettings("iPhone", maxsize, TextureImporterFormat.PVRTC_RGB4);

        AssetDatabase.ImportAsset(_relativeAssetPath, ImportAssetOptions.ForceUpdate);
    }
    #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
}

 

拿一个普通的Sprite用的Shader举例

Shader "Hidden/Unlit/Transparent Colored(RGBA) 1"
{
    Properties
    {
        _MainTex ("Base (RGB), Alpha (A)", 2D) = "black" {}
        _AlphaTex ("Alpha (A)", 2D) = "black" {}
    }

    SubShader
    {
        LOD 200

        Tags
        {
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
        }
        
        Pass
        {
            Cull Off
            Lighting Off
            ZWrite Off
            Offset -1, -1
            Fog { Mode Off }
            ColorMask RGB
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            sampler2D _AlphaTex;
            float4 _ClipRange0 = float4(0.0, 0.0, 1.0, 1.0);
            float2 _ClipArgs0 = float2(1000.0, 1000.0);

            struct appdata_t
            {
                float4 vertex : POSITION;
                half4 color : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : POSITION;
                half4 color : COLOR;
                float2 texcoord : TEXCOORD0;
                float2 worldPos : TEXCOORD1;
            };

            v2f o;

            v2f vert (appdata_t v)
            {
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.color = v.color;
                o.texcoord = v.texcoord;
                o.worldPos = v.vertex.xy * _ClipRange0.zw + _ClipRange0.xy;
                return o;
            }

            half4 frag (v2f IN) : COLOR
            {
                // Softness factor
                float2 factor = (float2(1.0, 1.0) - abs(IN.worldPos)) * _ClipArgs0;
            
                // Sample the texture
                half4 col = half4(tex2D(_MainTex, IN.texcoord).rgb,tex2D(_AlphaTex, IN.texcoord).r) * IN.color;
                
                col.a *= clamp( min(factor.x, factor.y), 0.0, 1.0);
                return col;
            }
            ENDCG
        }
    }
    
    SubShader
    {
        LOD 100

        Tags
        {
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
        }
        
        Pass
        {
            Cull Off
            Lighting Off
            ZWrite Off
            Fog { Mode Off }
            ColorMask RGB
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMaterial AmbientAndDiffuse
            
            SetTexture [_MainTex]
            {
                Combine Texture * Primary
            }
        }
    }
}

 

以上是关于Unity3D 优化NGUI纹理的主要内容,如果未能解决你的问题,请参考以下文章

Unity3D移动端内存优化(NGUI方面)

Unity3d 在NGUI中实现 图片的切换 求代码

Unity3D的NGUI插件导入工程后提示错误:UnityEngine.QualitySettings、 GetQualityLevel

如何在片段着色器中平铺部分纹理

在Unity3D中实现高效的战斗飘字

unity3D ngui 插件 关于隐藏和显示游戏对象的问题