200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > Unity项目优化-Alpha通道分离

Unity项目优化-Alpha通道分离

时间:2019-10-23 01:04:31

相关推荐

Unity项目优化-Alpha通道分离

前段时间由于项目打包生成的apk太大,所以引入了Alpha通道分离,后面在用的时候发现实际效果不是那么理想就移除了,但还是觉得有必要记录一下

前言

我们都知道,一个图片有RGBA四个通道,其中R红色、G绿色、B蓝色、A透明度

如果一个图片包含透明像素,那么它就可以进行Alpha通道分离,将一张图片的RGB通道和Alpha通道分离开来

图片分离的Shader

目前项目使用的NGUI,将以下Shader从NGUI中拷贝至一个新的文件夹中,文件夹自己命名

Unlit - Transparent ColoredUnlit - Transparent Colored (TextureClip)Unlit - Transparent Colored 1Unlit - Transparent Colored 2Unlit - Transparent Colored 3

新的Shader我们重新命名一下,我在中间加了一个Devide(随意)

但是所有的Shader,需要有一样的开头,结尾则和NGUI一致,如下:(Devide可以替换)

Unlit - Transparent Colored DivideUnlit - Transparent Colored Divide (TextureClip)Unlit - Transparent Colored Divide 1Unlit - Transparent Colored Divide 2Unlit - Transparent Colored Divide 3

这样做的原因,是因为我们需要替换NGUIAtlas的Material上面的Shader

这是NGUI原有的Shader之一,Unlit - Transparent Colored 1

在NGUI的UIDrawCall.cs中,有一段代码

我们可以发现,它是按照”Hidden/Unlit/xxxxx x”前缀来动态查找对应的Shader使用的

if (shaderName.StartsWith("Hidden/"))shaderName = shaderName.Substring(7);// Legacy functionalityconst string soft = " (SoftClip)";shaderName = shaderName.Replace(soft, "");const string textureClip = " (TextureClip)";shaderName = shaderName.Replace(textureClip, "");if (panel != null && panel.clipping == Clipping.TextureMask){mTextureClip = true;shader = Shader.Find("Hidden/" + shaderName + textureClip);}else if (mClipCount != 0){shader = Shader.Find("Hidden/" + shaderName + " " + mClipCount);if (shader == null) shader = Shader.Find(shaderName + " " + mClipCount);// Legacy functionalityif (shader == null && mClipCount == 1){mLegacyShader = true;shader = Shader.Find(shaderName + soft);}}else shader = Shader.Find(shaderName);

有以下几种情况是使用这些Shader的:

普通Sprite在UIPanel中的Sprite在UIPanel中的Sprite,以Texture遮盖在UIPanel中的Sprite,在边缘渐变过渡遮盖在UIPanel中的Sprite,在边缘直接遮盖

对应UIPanel选项如下:

由此可见上面查找的几种Shader,是会在这几个地方用到, 我们需要修改的Shader也正是这几个.

如果还有其他情况,那么找到对应的Shader拷贝过来修改就是了。

修改方法如下:

这是原ShaderUnlit/Transparent Colored,用来对比

Shader "Unlit/Transparent Colored"{Properties{_MainTex ("Base (RGB), Alpha (A)", 2D) = "black" {}}.........fixed4 frag (v2f IN) : SV_Target{// Sample the texturehalf4 col = tex2D(_MainTex, IN.texcoord) * IN.color;return col;}}

对应的Unlit/Transparent Colored Divide, 修改为:

Shader "Unlit/Transparent Colored Divide"{Properties{_MainTex ("Base (RGB), Alpha (A)", 2D) = "black" {}_AlphaTex("Alpha (A)",2D) = "white"{}}.........fixed4 frag (v2f IN) : SV_Target{fixed4 texcol = tex2D(_MainTex, IN.texcoord) * IN.color;//Alpha通道合并texcol.a *= tex2D(_AlphaTex, IN.texcoord).r; return texcol;}}

只是在Shader中增加了一个_AlphaTex

我们分离的时候将图片的Alpha值存入_Alpha图片的r值中,所以在合并的时候就用这个图片的r值来控制整个图片的a

至于其他的Shader,也是和这个一样的修改即可

图片Alpha分离

Alpha分离工具代码,直接选择UIAtlas的Prefab,执行以下代码就行

[MenuItem("Tools/Atlas/分离图集Alpha通道,生成新Material(选中图集Prefab)")]public static void DivideAlpha(){const string AlphaDevideShader = "Assets/Resources/Shaders/DivideAlpha/Unlit - Transparent Colored Divide.shader";//获取分离shaderShader shader = AssetDatabase.LoadAssetAtPath<Shader>(AlphaDevideShader);foreach (GameObject go in Selection.gameObjects){//获取图集UIAtlas atlas = go.GetComponent<UIAtlas>();if (atlas != null){//读取图集图片string path = AssetDatabase.GetAssetPath(go.GetInstanceID());string originTexPath = path.Replace("_RGB", string.Empty).Replace(".prefab", ".png"); // xxx_RGB.png -> xxx.pngDebug.Log(originTexPath);如果不含原图则退出if (!File.Exists(originTexPath)) break;//设置原图可编辑(RGBA) xxx.pngTextureImporter importer = TextureImporter.GetAtPath(originTexPath) as TextureImporter;importer.isReadable = true;importer.SaveAndReimport();//读取原图(RGBA)Texture2D source = AssetDatabase.LoadAssetAtPath<Texture>(originTexPath) as Texture2D;float sizeScale = 1;Texture2D rgbTex = new Texture2D(source.width, source.height, TextureFormat.RGB24, true);Texture2D alphaTex = new Texture2D((int)(source.width * sizeScale), (int)(source.height * sizeScale), TextureFormat.RGB24, true);Color[] rgbColors = new Color[source.width * source.height];Color[] alphaColors = new Color[source.width * source.height];for (int i = 0; i < source.width; ++i){for (int j = 0; j < source.height; ++j){Color color = source.GetPixel(i, j);Color rgbColor = color;if (color.a == 0.0f){rgbColor.r = 0;rgbColor.g = 0;rgbColor.b = 0;}rgbColor.a = 1;rgbColors[source.width * j + i] = color;Color alphaColor = color;alphaColor.r = color.a;alphaColor.g = color.a;alphaColor.b = color.a;alphaColor.a = 1;alphaColors[source.width * j + i] = alphaColor;}}rgbTex.SetPixels(rgbColors);alphaTex.SetPixels(alphaColors);rgbTex.Apply();alphaTex.Apply();//生成分离图片RGB + Alphabyte[] bytes = rgbTex.EncodeToPNG();File.WriteAllBytes(originTexPath.Replace(go.name + ".png", go.name + "_RGB.png"), bytes);bytes = alphaTex.EncodeToPNG();File.WriteAllBytes(originTexPath.Replace(go.name + ".png", go.name + "_A.png"), bytes);Texture rgbTextAsset = AssetDatabase.LoadAssetAtPath<Texture>(originTexPath.Replace(go.name + ".png", go.name + "_RGB.png"));Texture alphaTextAsset = AssetDatabase.LoadAssetAtPath<Texture>(originTexPath.Replace(go.name + ".png", go.name + "_A.png"));AssetDatabase.Refresh();生成新材质//Material material = new Material(shader);//AssetDatabase.CreateAsset(material, path.Replace(go.name + ".png", go.name + "_RGBA.mat"));//material.name = atlas.name + "_RGBA";应用分离图片//material.SetTexture("_MainTex", rgbTex);//material.SetTexture("_AlphaTex", alphaTex);//应用材质//atlas.spriteMaterial = material;//替换原材质shaderatlas.spriteMaterial.shader = shader;atlas.spriteMaterial.SetTexture("_MainTex", rgbTextAsset);atlas.spriteMaterial.SetTexture("_AlphaTex", alphaTextAsset);AssetDatabase.Refresh();}}AssetDatabase.SaveAssets();Debug.Log("finish");}

上面这个一键生成的代码,Material的texture挂上去了,但是在运行时会丢失,于是分成了几步

1 选中要分离的图片,执行下面的,生成xxx_RGB.png和xxx_Alpha.png

2 至于Material上的Shader和Texture,就手动挂吧

[MenuItem("Tools/Atlas/分离TextureAlpha通道")]public static void DivideTexture(){//获取图片string path = AssetDatabase.GetAssetPath(Selection.activeInstanceID);Debug.Log(path);//设置原图可编辑(RGBA) xxx.pngTextureImporter importer = TextureImporter.GetAtPath(path) as TextureImporter;importer.isReadable = true;importer.SaveAndReimport();//读取原图(RGBA)Texture2D source = AssetDatabase.LoadAssetAtPath<Texture>(path) as Texture2D;float sizeScale = 1;Texture2D rgbTex = new Texture2D(source.width, source.height, TextureFormat.RGB24, true);Texture2D alphaTex = new Texture2D((int)(source.width * sizeScale), (int)(source.height * sizeScale), TextureFormat.RGB24, true);Color[] rgbColors = new Color[source.width * source.height];Color[] alphaColors = new Color[source.width * source.height];for (int i = 0; i < source.width; ++i){for (int j = 0; j < source.height; ++j){Color color = source.GetPixel(i, j);Color rgbColor = color;if (color.a == 0.0f){rgbColor.r = 0;rgbColor.g = 0;rgbColor.b = 0;}rgbColor.a = 1;rgbColors[source.width * j + i] = color;Color alphaColor = color;alphaColor.r = color.a;alphaColor.g = color.a;alphaColor.b = color.a;alphaColor.a = 1;alphaColors[source.width * j + i] = alphaColor;}}rgbTex.SetPixels(rgbColors);alphaTex.SetPixels(alphaColors);rgbTex.Apply();alphaTex.Apply();//生成分离图片RGB + Alphabyte[] bytes = rgbTex.EncodeToPNG();File.WriteAllBytes(path.Replace(source.name + ".png", source.name + "_RGB.png"), bytes);bytes = alphaTex.EncodeToPNG();File.WriteAllBytes(path.Replace(source.name + ".png", source.name + "_A.png"), bytes);AssetDatabase.ImportAsset(path.Replace(source.name + ".png", source.name + "_RGB.png"));AssetDatabase.ImportAsset(path.Replace(source.name + ".png", source.name + "_A.png"));AssetDatabase.Refresh();AssetDatabase.Refresh();Debug.Log("finish");}

分离后的资源

RGBA的内存64k = RGB内存32k + Alpha内存32k

在实际情况中,如果图片越大,透明像素占比较大,Alpha内存是小于32k的,所以总内存有所减少

另一个方面,理论上Alpha图片可以缩小1/2的尺寸, 从而减少内存到8k,因为它含有的信息量低(只有r值有值),压缩尺寸后不会影响很大,但实际我也没尝试过。有兴趣的同学可以尝试一下。

- 分离前后的Material对比

总结

Alpha分离技术其实也就是将图片分离之后再利用Shader合并,不过在实际项目过程中发现效果不太理想,所以舍弃了。

而且Unity现在自己提供了图片打包并且支持分离Alpha通道,比这个更方便快捷,所以这个Alpha分离技术也只适用于使用NGUI的童鞋们了

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。