ETC1 格式在 OpenGL ES 2.0 时代的 Android 设备上不支持 Alpha 通道,这给带透明度的贴图(如 UI、特效、角色等)带来了挑战。下面详细说明原理、常见拆分方案、Shader 实现方式,以及 Unity 中的实际操作建议。
1. ETC1 不支持 Alpha 通道的原因
- ETC1 是一种只支持 RGB(不带 Alpha)的压缩纹理格式,广泛用于早期 Android 设备。
- 如果直接用 ETC1 存储带 Alpha 的图片,Alpha 信息会丢失,导致透明区域变黑或不透明。
2. 解决方案:Alpha 通道分离法
原理
- 将原始 RGBA 图片分成两张图:
- 一张 RGB 图(去掉 Alpha,保存为 ETC1 格式)
- 一张 Alpha 图(只保存 Alpha 通道,通常灰度图,也保存为 ETC1 格式)
资源导出流程
- 分离通道:美术或工具将一张 RGBA 图片分成两张 PNG(或 TGA):
xxx_rgb.png
:只保留 RGB,Alpha 全为 1xxx_a.png
:只保留 Alpha,RGB 全为 0,Alpha 通道为原图 Alpha
- 压缩:分别用 ETC1 格式压缩这两张图。
- 打包:在资源管理中将这两张图作为一组。
3. Shader 合成实现
基本思路
- 在 Shader 中分别采样 RGB 贴图和 Alpha 贴图。
- 用 RGB 贴图的 rgb,Alpha 贴图的 r(或 a)作为最终输出的 alpha。
伪代码(GLSL/HLSL)
uniform sampler2D _MainTex; // RGB 贴图
uniform sampler2D _AlphaTex; // Alpha 贴图
void main()
{
vec4 color = texture2D(_MainTex, uv);
float alpha = texture2D(_AlphaTex, uv).r; // 通常用 r 通道
gl_FragColor = vec4(color.rgb, alpha);
}
Unity Shader 示例
Shader "Custom/ETC1AlphaSplit"
{
Properties
{
_MainTex ("RGB Texture", 2D) = "white" {}
_AlphaTex ("Alpha Texture", 2D) = "white" {}
}
SubShader
{
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
sampler2D _AlphaTex;
struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; };
struct v2f { float2 uv : TEXCOORD0; float4 pos : SV_POSITION; };
v2f vert(appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; }
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
fixed4 alpha = tex2D(_AlphaTex, i.uv);
col.a = alpha.r;
return col;
}
ENDCG
}
}
}
4. Unity 实际操作建议
- 自动拆分工具:可用脚本或美术工具自动拆分 RGBA 到 RGB/Alpha 两张图。
- 打包管理:用 AssetBundle、Addressable 或自定义资源管理,保证两张图一一对应。
- Shader 适配:UI、特效等用自定义 Shader 支持双贴图采样。
- 平台兼容:新设备支持 ETC2/ASTC/ETC1+Alpha/Uncompressed,可用平台宏自动切换 Shader 或贴图加载方式。
5. 进阶方案
- Alpha 通道打包到 RGB 的某一通道:如多张 Alpha 合成一张 RGB,节省带宽,但采样时要注意通道选择。
- ETC1+A(Unity 5.5+):Unity 内置支持 ETC1 Alpha Split,勾选“Build Alpha from Grayscale”即可自动拆分和合成,Shader 里用
UNITY_DECLARE_TEX2D(_MainTex); UNITY_DECLARE_TEX2D(_MainTexAlpha);
等宏。
6. 参考资料
下面我将继续深入,补充ETC1不支持Alpha通道的相关内容,包括自动化流程、Unity平台的具体实现细节、性能注意事项、常见问题与进阶技巧。
1. Unity平台的自动Alpha分离与合成
1.1 Unity内置支持(ETC1 + Alpha Split)
从Unity 5.5开始,Unity已经内置了ETC1的Alpha分离方案:
- Texture Importer设置
- 选择你的纹理,设置为Android平台,压缩格式选ETC(RGB),
- 勾选**“Alpha is Transparency”**(或“Build Alpha from Grayscale”)。
- Unity自动处理
- Unity会自动将Alpha通道拆分为一张单独的Alpha贴图(通常命名为
_Alpha
后缀),并在打包时一同导出。 - Shader中通过宏
UNITY_DECLARE_TEX2D(_MainTex); UNITY_DECLARE_TEX2D(_MainTexAlpha);
来访问主贴图和Alpha贴图。
- Unity会自动将Alpha通道拆分为一张单独的Alpha贴图(通常命名为
1.2 Shader自动适配
Unity的标准Shader在Android ETC1平台下会自动切换到双贴图采样模式,无需手动修改Shader。
如果你用自定义Shader,需手动采样Alpha贴图并合成。
2. 自动化拆分与打包流程
2.1 美术资源自动拆分脚本(Python/PIL示例)
from PIL import Image
def split_alpha(input_path, rgb_path, alpha_path):
img = Image.open(input_path).convert('RGBA')
rgb = Image.new('RGB', img.size, (0,0,0))
rgb.paste(img, mask=img.split()[3])
rgb.save(rgb_path)
alpha = img.split()[3]
alpha.save(alpha_path)
# 用法
split_alpha('input.png', 'output_rgb.png', 'output_a.png')
2.2 Unity Editor自动拆分(C#)
可用AssetPostprocessor
自动拆分导入的PNG为RGB和Alpha两张图,或用自定义Editor工具批量处理。
3. 性能与内存注意事项
- 带宽消耗:双贴图采样会增加带宽消耗(两次采样),但比RGBA未压缩要节省内存。
- 内存占用:两张ETC1贴图的总内存约等于一张RGBA未压缩贴图的1/4~1/2。
- DrawCall:不会增加DrawCall,只是多了一次采样。
- Shader复杂度:增加一次采样和一次合成,性能影响较小。
4. 常见问题与解决办法
4.1 贴图边缘出现黑边/白边
- 由于Alpha贴图和RGB贴图压缩误差不同步,边缘可能出现色彩溢出。
- 解决办法:美术导出时对RGB和Alpha都做膨胀(Dilate)处理,或用预乘Alpha(Premultiplied Alpha)。
4.2 贴图对齐问题
- ETC1压缩有时会导致像素对齐误差,务必保证RGB和Alpha贴图尺寸、UV完全一致。
4.3 资源管理
- 保证RGB和Alpha贴图一一对应,避免加载错位。
- 可用AssetBundle、Addressable等方式自动绑定。
5. 进阶技巧
5.1 多Alpha合一
- 多张Alpha通道可合成一张RGB贴图(如R通道存A1,G通道存A2,B通道存A3),节省内存,Shader采样时按需取用。
5.2 ETC2/ASTC等新格式
- 新设备支持ETC2/ASTC等格式,原生支持RGBA压缩,无需分离Alpha。
- 可用平台宏
#if UNITY_ANDROID && !UNITY_EDITOR
等自动切换加载和Shader逻辑。
5.3 运行时合成
- 某些特殊需求下,可在运行时用脚本合成RGB和Alpha贴图为一张RGBA贴图(如RenderTexture),但一般不推荐,因会增加CPU/GPU负担。