unity中, 将图集的 alpha 通道剥离出来可减少包体大小和内存使用大小。
方法是将原来的一张 rgba 图分成一张 rgb 和一张 alpha 图,android上rgb和alpha图均采用etc压缩格式,ios上采用pvrtc格式。其中alpha通道信息可以存在r中。
分享 alpha 通道工具的源代码如下:
1 using System; 2 using System.IO; 3 using UnityEditor; 4 using UnityEngine; 5 6 public static class TextureAlphaSpliter 7 { 8 const string 9 RGBEndName = "(rgb)", 10 AlphaEndName = "(a)"; 11 12 public static bool WhetherSplit = true; 13 public static bool AlphaHalfSize; 14 15 static Texture s_rgba; 16 17 public static void SplitAlpha(Texture src, bool alphaHalfSize, out Texture rgb, out Texture alpha) 18 { 19 if (src == null) 20 throw new ArgumentNullException("src"); 21 22 // make it readable 23 string srcAssetPath = AssetDatabase.GetAssetPath(src); 24 var importer = (TextureImporter)AssetImporter.GetAtPath(srcAssetPath); 25 { 26 importer.isReadable = true; 27 importer.SetPlatformTextureSettings("Standalone", 4096, TextureImporterFormat.ARGB32, 100, true); 28 importer.SetPlatformTextureSettings("Android", 4096, TextureImporterFormat.ARGB32, 100, true); 29 importer.SetPlatformTextureSettings("iPhone", 4096, TextureImporterFormat.ARGB32, 100, true); 30 } 31 AssetDatabase.ImportAsset(srcAssetPath); 32 33 alpha = CreateAlphaTexture((Texture2D)src, alphaHalfSize); 34 rgb = CreateRGBTexture(src); 35 } 36 37 static Texture CreateRGBTexture(Texture src) 38 { 39 if (src == null) 40 throw new ArgumentNullException("src"); 41 42 string srcPath = AssetDatabase.GetAssetPath(src); 43 string rgbPath = GetPath(src, RGBEndName); 44 int size = Mathf.Max(src.width, src.height, 32); 45 46 AssetDatabase.DeleteAsset(rgbPath); 47 AssetDatabase.CopyAsset(srcPath, rgbPath); 48 AssetDatabase.ImportAsset(rgbPath); 49 50 SetSettings(rgbPath, size, TextureImporterFormat.ETC_RGB4, TextureImporterFormat.PVRTC_RGB4); 51 52 return (Texture)AssetDatabase.LoadAssetAtPath(rgbPath, typeof(Texture)); 53 } 54 55 static Texture CreateAlphaTexture(Texture2D src, bool alphaHalfSize) 56 { 57 if (src == null) 58 throw new ArgumentNullException("src"); 59 60 // create texture 61 var srcPixels = src.GetPixels(); 62 var tarPixels = new Color[srcPixels.Length]; 63 for (int i = 0; i < srcPixels.Length; i++) 64 { 65 float r = srcPixels[i].a; 66 tarPixels[i] = new Color(r, r, r); 67 } 68 69 Texture2D alphaTex = new Texture2D(src.width, src.height, TextureFormat.ARGB32, false); 70 alphaTex.SetPixels(tarPixels); 71 alphaTex.Apply(); 72 73 // save 74 string saveAssetPath = GetPath(src, AlphaEndName); 75 string fullPath = BuildingAssetBundle.GetFullPath(saveAssetPath); 76 var bytes = alphaTex.EncodeToPNG(); 77 File.WriteAllBytes(fullPath, bytes); 78 79 AssetDatabase.SaveAssets(); 80 AssetDatabase.Refresh(); 81 82 // setting 83 int size = alphaHalfSize ? Mathf.Max(src.width / 2, src.height / 2, 32) : Mathf.Max(src.width, src.height, 32); 84 SetSettings(saveAssetPath, size, TextureImporterFormat.ETC_RGB4, TextureImporterFormat.PVRTC_RGB4); 85 86 return (Texture)AssetDatabase.LoadAssetAtPath(saveAssetPath, typeof(Texture)); 87 } 88 89 static void SetSettings(string assetPath, int maxSize, TextureImporterFormat androidFormat, TextureImporterFormat iosFormat) 90 { 91 var importer = (TextureImporter)AssetImporter.GetAtPath(assetPath); 92 { 93 importer.npotScale = TextureImporterNPOTScale.ToNearest; 94 importer.isReadable = false; 95 importer.mipmapEnabled = false; 96 importer.alphaIsTransparency = false; 97 importer.wrapMode = TextureWrapMode.Clamp; 98 importer.filterMode = FilterMode.Bilinear; 99 importer.anisoLevel = 4; 100 importer.SetPlatformTextureSettings("Android", maxSize, androidFormat, 100, true); 101 importer.SetPlatformTextureSettings("iPhone", maxSize, iosFormat, 100, true); 102 importer.SetPlatformTextureSettings("Standalone", maxSize, TextureImporterFormat.ARGB32, 100, true); 103 } 104 AssetDatabase.ImportAsset(assetPath); 105 } 106 107 static string GetPath(Texture src, string endName) 108 { 109 if (src == null) 110 throw new ArgumentNullException("src"); 111 112 string srcAssetPath = AssetDatabase.GetAssetPath(src); 113 if (string.IsNullOrEmpty(srcAssetPath)) 114 return null; 115 116 string dirPath = Path.GetDirectoryName(srcAssetPath); 117 string ext = Path.GetExtension(srcAssetPath); 118 string fileName = Path.GetFileNameWithoutExtension(srcAssetPath); 119 120 if (fileName.EndsWith(RGBEndName)) 121 fileName = fileName.Substring(0, fileName.Length - RGBEndName.Length); 122 123 if (fileName.EndsWith(AlphaEndName)) 124 fileName = fileName.Substring(0, fileName.Length - AlphaEndName.Length); 125 126 return string.Format("{0}/{1}{2}{3}", dirPath, fileName, endName ?? "", ext); 127 } 128 129 public static Texture GetRGBA(Texture src) 130 { 131 if (src != null && (s_rgba == null || s_rgba.name != src.name)) 132 { 133 string path = GetPath(src, ""); 134 if (!string.IsNullOrEmpty(path)) 135 s_rgba = AssetDatabase.LoadAssetAtPath(path, typeof(Texture)) as Texture; 136 } 137 138 return s_rgba; 139 } 140 }
然后改造一下 shader,以ngui的shader为例。
原来要用一张 rgba 图的地方,现在改成用两张图,一张 rgb 和一张 alpha,改造为下:
Shader "Custom/Alpha Splited Colored" { Properties { _MainTex ("RGB", 2D) = "black" {} _AlphaTex ("Alpha", 2D) = "black" {} } SubShader { LOD 200 Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } Pass { Cull Off Lighting Off ZWrite Off Fog { Mode Off } Offset -1, -1 Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag sampler2D _MainTex; sampler2D _AlphaTex; struct appdata_t { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; fixed4 color : COLOR; }; struct v2f { float4 vertex : SV_POSITION; half2 texcoord : TEXCOORD0; fixed4 color : COLOR; }; v2f vert (appdata_t v) { v2f o; o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); o.texcoord = v.texcoord; o.color = v.color; return o; } fixed4 frag (v2f IN) : COLOR { // [dev] fixed4 col; col.rgb = tex2D(_MainTex, IN.texcoord).rgb; col.a = tex2D(_AlphaTex, IN.texcoord).r; if (IN.color.r < 0.001 && IN.color.g < 0.001 && IN.color.b < 0.001) { float grey = dot(col.rgb, float3(0.299, 0.587, 0.114)); col.rgb = float3(grey, grey, grey); } else col = col * IN.color; return col; } ENDCG } } SubShader { LOD 100 Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } Pass { Cull Off Lighting Off ZWrite Off Fog { Mode Off } Offset -1, -1 ColorMask RGB Blend SrcAlpha OneMinusSrcAlpha ColorMaterial AmbientAndDiffuse SetTexture [_MainTex] { Combine Texture * Primary } SetTexture [_AlphaTex] { Combine previous, texture * primary } } } }