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