虽然手游内容越来越丰富,手游安装包资源包越来越大,却不能否认开发者们一直在努力地缩减资源包容量。以玩家为之痴狂的精美游戏贴图的处理为例,在多方权衡采用合理尺寸的同时,还在采用各种历史悠久的久经考验的高效压缩算法来进一步压缩贴图,譬如十数年前创立的ETC1压缩格式,依然被最广泛地应用在各种Android机器上。但是,ETC1居然不支持贴图的Alpha通道…… 多么坑爹坑娘的设定,为了这份原罪,无数可怜可爱的程序猿程序媛们要为ETC1的Alpha通道处理多一些辛劳。
废话不能再多了,总而言之,为了解决Android上ETC1压缩格式不支持Alpha通道的问题,你可以选择用RGBA格式绕开这个问题,为缩减容量用R4G4B4A4也可以。我们依然选择无悔地追随ETC1,既然ETC1不支持Alpha通道,那么我们用一个RGB格式的图片存储Alpha信息吧。于是引出了本文的标题:拆分贴图的Alpha通道。
拆分贴图的RGB和Alpha通道信息
将含RGBA信息的贴图拆分两张RGB贴图,分别存放原贴图的RGB信息和Alpha信息。RGB信息图直接舍弃原Alpha通道信息即可,RGB值维持不变;Alpha信息图则是RGB值同时设置为原图片的Alpha值,最后的结果为一张灰度图。
(a) 原RGBA图片 (b) RGB信息图 (c) Alpha信息图
具体如何拆分,有很多实现方式,鉴于现在大多项目用Unity开发,直接在Unity里写个脚本就可以处理,譬如下图:
代码简单直白自解释,不再啰嗦。
Shader和Material的对应处理
原RGBA贴图被拆分为两张贴图后,使用此贴图的Material和Shader也需要作相应调整。
以NGUI的Unlit - Transparent Colored.shader为例,
Properties里添加Alpha信息图片的输入口:
_MainTex_A ("Alpha ( Alpha )", 2D) = "white" {}
对应添加sampler2D _MainTex_A;
最后将frag()里对于颜色的计算调整为:
#define TEX2D_ALPHA( sampler, tex ) float4( tex2D( sampler, tex ).rgb, tex2D( sampler##_A, tex ).r )
fixed4 frag (v2f IN) : COLOR
{
return TEX2D_ALPHA(_MainTex, IN.texcoord) * IN.color;
}
Shader数量比较多的情况下,不妨写个文本替换脚本批量转换。
修改完Shader,编辑Material时就能将对应的RGB和Alpha信息图引用到对应的输入位置。
贴图处理的时机
什么时候处理贴图?
比较直观的做法是,平常的游戏开发过程中还是用包含完整RGBA信息的贴图,在构建版本时批量处理Material,将所有引用的有Alpha通道的贴图进行Alpha通道的拆分,同时处理Shader和修改Material的属性。这样的好处是平时开发时处理的图片量较少,且大多数美术和程序对贴图的拆分无感,缺点是加大了构建耗时,每次构建都需要花相当比例的时间(就笔者经历的一个项目,贴图处理耗时占整个构建时长的15%左右。)处理材质和贴图,构建完成之后Revert回先前状态。
为了避免每次构建时重复的贴图处理工作,可以将贴图处理工作移到平时的开发过程中。游戏场景、模型、特效等用到的贴图,可以要求美术在制作贴图时就将RGB和Alpha信息分开存储为两张贴图,编辑材质时正确使用RGB和Alpha贴图即可。对于前期已存在的RGBA贴图,程序可以提供小工具供美术批量拆分贴图的Alpha通道。对于UI贴图的处理,比较复杂一点。见下文。
UI贴图的处理
笔者对UGUI研究不多,下面只以目前项目中常用的NGUI工具为例说明UI贴图的处理。
NGUI存在图集概念,将美术制作的小散图合并为带Alpha通道的大图,也就是我们使用的图集。为了在平时的开发中就能使用拆分Alpha通道的UI贴图,必须改造NGUI,主要是UIAtlasMaker模块,修改生成Atlas贴图的功能,让美术在制作Atlas时自动生成对应的RGB和Alpha信息贴图。
NGUI的改造
1. 根据NGUI原本生成的altas贴图生成对应RGB和Alpha信息图,以及修改第一次创建时生成的Material的属性。此功能的改造位于UIAtlasMaker.cs:
2. 更新Atlas时,需要保证获取的小图信息是完整的,即必须从NGUI原本生成的RGBA贴图里获取完整贴图信息。因此需要修改UIAtlasMaker模块的函数:
3. 为了编辑UISprite时能够看到完整的RGBA信息,而不是看着怪怪的单独的RGB信息,需要修改SpriteSelector.cs。
用改造后的NGUI创建图集,会生成5个文件,多出的两个文件是拆分Alpha通道得到的RGB信息图和Alpha信息图。 原生NGUI生成的完整RGBA信息的贴图还是需要保留,在图集的更新以及UISprite的编辑时会用到。游戏运行过程中,只需要用到拆分后的两张图,原RGBA贴图不会被载入内存。
Shader用上文提到的方法,写个文本处理脚本批量转换,一次转换,永久生效。(龇牙状)嗯,Shader的处理应该在UIAtlasMaker改造之前。
写在最后
虽然贴图Alpha通道拆分的需求是由ETC1引起的,但事实上,将Alpha通道信息拆分出来单独存储后,也有益于IOS平台上PVRTC压缩格式的表现效果,感兴趣的同学可以深究PVRTC的压缩算法。
另外,Alpha信息图的处理,还有优化空间,Alpha信息的熵较低,存储空间应该可以减少一些。笔者没有深究了,感兴趣的同学研究后不妨和笔者分享下,哈哈。