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


前言

我们都知道,一个图片有RGBA四个通道,其中R红色、G绿色、B蓝色、A透明度
如果一个图片包含透明像素,那么它就可以进行Alpha通道分离,将一张图片的RGB通道和Alpha通道分离开来


图片分离的Shader

  • 目前项目使用的NGUI,将以下Shader从NGUI中拷贝至一个新的文件夹中,文件夹自己命名
Unlit - Transparent Colored
Unlit - Transparent Colored (TextureClip)
Unlit - Transparent Colored 1
Unlit - Transparent Colored 2
Unlit - Transparent Colored 3
  • 新的Shader我们重新命名一下,我在中间加了一个Devide(随意)
    但是所有的Shader,需要有一样的开头,结尾则和NGUI一致,如下:(Devide可以替换)
Unlit - Transparent Colored Divide
Unlit - Transparent Colored Divide (TextureClip)
Unlit - Transparent Colored Divide 1
Unlit - Transparent Colored Divide 2
Unlit - 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 functionality
const 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的:
  1. 普通Sprite
  2. 在UIPanel中的Sprite
  3. 在UIPanel中的Sprite,以Texture遮盖
  4. 在UIPanel中的Sprite,在边缘渐变过渡遮盖
  5. 在UIPanel中的Sprite,在边缘直接遮盖
  • 对应UIPanel选项如下:

    由此可见上面查找的几种Shader,是会在这几个地方用到, 我们需要修改的Shader也正是这几个.
    如果还有其他情况,那么找到对应的Shader拷贝过来修改就是了。

  • 修改方法如下:
    这是原Shader Unlit/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的童鞋们了

Unity项目优化-Alpha通道分离相关推荐

  1. unity recorder输出带alpha通道图像

    首先选中相机 如下图 1.给相机设置tag 2.clearFlags 选择 solidColor 模式 3 mask 选择指定的渲染的层级 要渲染的物体设置如下 1 l指定layer 和相机的渲染的层 ...

  2. Unity项目优化详解(持续补充ing)

    Unity开发项目总结的几项优化点,比较适合中小项目优化,拿来即用,大型项目需要考虑定制化渲染管线.剔除.光照等.针对优化更多的还是需要结合项目去考虑. 一.模型 Read/Write:同Textur ...

  3. Unity 项目优化细节

    优化思路 个人优化原则: 三原则: 注意细节.注意细节.注意细节! 优化手段: 1.善于使用工具 2.减少总量 3.空间.时间互换 4.由浅入深 1.善于使用工具: 一定要善于使用工具来分析性能问题( ...

  4. 【Unity项目优化宝典】Unity3d打包后移动端启动黑屏时间太久

    欢迎加入Unity业内qq交流群:956187480 qq扫描二维码加群,行业纵横颇多,每个人精通领域各异,旨在交流, .公司性质使然,经手很多小项目.在美术资源少,代码框架简单的情况下一般不会考虑到 ...

  5. 【Unity项目优化宝典】静态批处理和动态批处理

    欢迎加入Unity业内qq交流群:956187480 qq扫描二维码加群 一:Batching批处理须知 1.如果多个对象使用了同一个材质,则unity会一次性将使用了同一材质的对象绘制信息传递给GP ...

  6. Unity播放带Alpha通道的视频【WebM+Video Player】(替代播放GIF方案)

    在Unity中播放GIF或者动态效果,可以通过Video Player播放带透明通道的WebM视频来实现. 制作带Alpha的MOV视频 制作带Alpha通道的MOV视频有多重方式,如AE.PR.PS ...

  7. U3D 贴图通道分离后为什么能减小体积

    U3D 贴图通道分离后为什么能减小体积 原理上,分离与否,不会减小图片原始体积,还可能增大了. RGBA32 分离后 = RGB24 + A8,这种情况下大小没变 但压缩后就不一样了,因为RGBA32 ...

  8. unity加载sprite_Unity 分离贴图 alpha 通道实践

    引言 在做手机游戏时可能会遇到这些问题: UI 同学天天抱怨 iOS 上一些透明贴图压缩后模糊不堪 一些古早的 Android 手机上同样的贴图吃内存超过其他手机数倍,游戏经常闪退 这篇文章给出了一种 ...

  9. Unity 分离贴图 alpha 通道实践

    在做手机游戏时可能会遇到这些问题: UI 同学天天抱怨 iOS 上一些透明贴图压缩后模糊不堪 一些古早的 Android 手机上同样的贴图吃内存超过其他手机数倍,游戏经常闪退 这篇文章给出了一种手机游 ...

最新文章

  1. objective-c对NSArray的学习
  2. windows创建定时任务执行python脚本
  3. ORA-01034: ORACLE not available ORA-27101
  4. centos7 ssh 密码拒绝_Centos7的ssh connection refused
  5. Oracle中SQL解析的流程
  6. Django(补充CBV,FBV)
  7. 报错空指针异常_让你为之颤抖的Java常见的异常exception
  8. phpstudy使用(80端口被system占用,无法关闭和删除)
  9. 教室信息管理系统mysql_教师信息管理系统(方式一:数据库为oracle数据库;方式二:存储在文件中)...
  10. 计算机网络技术应用和发展,计算机网络技术的应用和发展研究
  11. 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-3. 菜单)...
  12. zabbix 监控项自动发现过滤_zabbix怎么使用自动发现添加新监控项
  13. 计算机应用excel题,计算机应用操作练习题-Excel
  14. 使用three.js创建一个正方体
  15. 解决 winedit 打开tex文件 reading error(亲测可行)
  16. 腾讯AI加速器招募再启,AI开放既是工具箱也是方法库?
  17. vnr光学识别怎么打开_SLS46CK4单光束安全传感器原版使用说明-Leuzeelectronic.PDF
  18. 算法竞赛入门经典 例题6-21
  19. 一分耕耘一分收获,精诚所至金石为开
  20. CCPC-Wannafly Winter Camp Day8 (Div2, onsite)

热门文章

  1. stata画时间趋势图时横坐标标签太长重叠怎么办
  2. 11.1 p值的意义
  3. 网络安全“攻防战”:“魔”“道”大盘点
  4. vector容器的动态分配空间
  5. ICRA2022 SLAM相关论文整理
  6. vue将数据导出到excel
  7. 华为一级产品线介绍(部分)
  8. WKWebView白屏问题
  9. 51nod1503 猪和回文
  10. 牛客2019跨年AK场题解(一)