Unity工程里图片的RGB和Alpha通道的分离,以及显示所有带有Alpha通道贴图的Material

背景:ETC1图片格式的罪孽,不支持Alpha通道。于是程序员们将一些气力浪费在Alpha通道的处理上。 为了能使用ETC1,同时某些透明效果必须有Alpha通道,一般的处理方式是将RGB和Alpha分为两张图片分别储存。 只存Alpha通道的图片及RGB都为要存的Alpha值,因为熵比较小,图片尺寸也可以相应减小一些。

要做的工作:

1. 将带有Alpha通道的图片,另存为两张图片,一张只存RGB信息,另一张只存Alpha信息。建议保持为图片原目录,名称加后缀“_RGB”, "_Alpha"。

2. 带有Alpha通道的图片,所用的Shader要更新为支持RGB和Alpha信息分别从两张不同图片读取的shader。这个功能,因为不能的Material的Shader会很不一样,因此,不用程序来强硬指定了。但程序起码需要给出提示,工程中哪些Material用到了哪些带有Alpha通道的图片。

不罗嗦了,直接上代码。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System.IO;
using System.Reflection;

public class MaterialTextureForETC1{

	public static float sizeScale = 0.5f;   //the size decrease scale for alphaTexture
	public static Dictionary texturesAlphaDic = new Dictionary();

	[MenuItem("EffortForETC1/Seperate RGB and Alpha Channel for All Textures")]
	static void SeperateAllTexturesRGBandAlphaChannel()
	{
		string[] paths = Directory.GetFiles(Application.dataPath, "*.*", SearchOption.AllDirectories);
		foreach (string path in paths)
		{
			if (!string.IsNullOrEmpty(path) && IsTextureFile(path))   //full name
			{
				SeperateRGBAandlphaChannel(path);
			}
		} 
	}

	[MenuItem("EffortForETC1/Show Materials Having Textures with Alpha Chanel")]
	static void ShowMaterialsHavingTextureswithAlphaChanel()
	{
		CalculateTexturesAlphaChannelDic();
		string[] matpaths = Directory.GetFiles(Application.dataPath, "*.mat", SearchOption.AllDirectories);
		foreach (string matpath in matpaths)
		{
			string propermatpath = GetRelativeAssetPath(matpath);
			Material mat = (Material)AssetDatabase.LoadAssetAtPath(propermatpath, typeof(Material));
			if (mat)
			{
				string[] alphatexpaths = GetMaterialTexturesHavingAlphaChannel(mat);
				if (alphatexpaths.Length == 0)
				{
					continue;
				}
				Debug.Log("Material having texture(s) with Alpha channel : " + propermatpath);
				foreach (string alphatexpath in alphatexpaths)
				{
					Debug.Log(alphatexpath + " in " + propermatpath);
				}
			}
			else
			{
				Debug.LogError("Load material failed : " + matpath);
			}
		}
		Debug.Log("Finish!");     
	}


	#region inspect material

	static string[] GetMaterialTexturesHavingAlphaChannel(Material _mat)
	{
		List alphatexpaths = new List();
		string[] texpaths = GetMaterialTexturePaths(_mat);
		foreach (string texpath in texpaths)
		{
			if (texturesAlphaDic[texpath])
			{
				alphatexpaths.Add(texpath);
			}
		}

		return alphatexpaths.ToArray();
	}

	static string[] GetMaterialTexturePaths(Material _mat)
	{
		List results = new List();
		Object[] roots = new Object[] { _mat };
		Object[] dependObjs = EditorUtility.CollectDependencies(roots);
		foreach (Object dependObj in dependObjs)
		{
			if (dependObj.GetType() == typeof(Texture2D))
			{
				string texpath = AssetDatabase.GetAssetPath(dependObj.GetInstanceID());
				results.Add(texpath);
			}
		}
		return results.ToArray();
	}

	#endregion


	static void CalculateTexturesAlphaChannelDic()
	{
		string[] paths = Directory.GetFiles(Application.dataPath, "*.*", SearchOption.AllDirectories);
		foreach (string path in paths)
		{
			if (!string.IsNullOrEmpty(path) && IsTextureFile(path))   //full name
			{
				string assetRelativePath = GetRelativeAssetPath(path);
				SetTextureReadable(assetRelativePath);
				Texture2D sourcetex = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(Texture2D)) as Texture2D;  
				if (!sourcetex)  //make sure the file is really Texture2D which can be loaded as Texture2D.
				{
					continue;
				}
				if (HasAlphaChannel(sourcetex))
				{
					AddValueToDic(assetRelativePath, true);                                    
				}
				else
				{
					AddValueToDic(assetRelativePath, false);  
				}
			}
		} 
	}

	static void AddValueToDic(string _key, bool _val)
	{
		if (texturesAlphaDic.ContainsKey(_key))
		{
			texturesAlphaDic[_key] = _val;
		}
		else
		{
			texturesAlphaDic.Add(_key, _val);
		}
	}


	#region process texture

	static void SeperateRGBAandlphaChannel(string _texPath)
	{
		string assetRelativePath = GetRelativeAssetPath(_texPath);
		SetTextureReadable(assetRelativePath);
		Texture2D sourcetex = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(Texture2D)) as Texture2D;  //not just the textures under Resources file
		if (!sourcetex)
		{
			Debug.Log("Load Texture Failed : " + assetRelativePath);
			return;
		}
		if (!HasAlphaChannel(sourcetex))
		{
			Debug.Log("Texture does not have Alpha channel : " + assetRelativePath);
			return;
		}

		Texture2D rgbTex = new Texture2D(sourcetex.width, sourcetex.height, TextureFormat.RGB24, true);
		Texture2D alphaTex = new Texture2D((int)(sourcetex.width * sizeScale), (int)(sourcetex.height * sizeScale), TextureFormat.RGB24, true);

		for (int i = 0; i < sourcetex.width; ++i)
			for (int j = 0; j < sourcetex.height; ++j)
			{
				Color color = sourcetex.GetPixel(i, j);
				Color rgbColor = color;
				Color alphaColor = color;
				alphaColor.r = color.a;
				alphaColor.g = color.a;
				alphaColor.b = color.a;
				rgbTex.SetPixel(i, j, rgbColor);
				alphaTex.SetPixel((int)(i * sizeScale), (int)(j * sizeScale), alphaColor);
			}

		rgbTex.Apply();
		alphaTex.Apply();

		byte[] bytes = rgbTex.EncodeToPNG();
		File.WriteAllBytes(GetRGBTexPath(_texPath), bytes);
		bytes = alphaTex.EncodeToPNG();
		File.WriteAllBytes(GetAlphaTexPath(_texPath), bytes);
		Debug.Log("Succeed to seperate RGB and Alpha channel for texture : " + assetRelativePath);
	}

	static bool HasAlphaChannel(Texture2D _tex)
	{
		for (int i = 0; i < _tex.width; ++i)
			for (int j = 0; j < _tex.height; ++j)
			{
				Color color = _tex.GetPixel(i, j);
				float alpha = color.a;
				if (alpha < 1.0f - 0.001f)
				{
					return true;
				}
			}
		return false;
	}

	static void SetTextureReadable(string _relativeAssetPath)
	{
		string postfix = GetFilePostfix(_relativeAssetPath);
		if (postfix == ".dds")    // no need to set .dds file.  Using TextureImporter to .dds file would get casting type error.
		{
			return;
		}

		TextureImporter ti = (TextureImporter)TextureImporter.GetAtPath(_relativeAssetPath);
		ti.isReadable = true;
		AssetDatabase.ImportAsset(_relativeAssetPath);
	}

	#endregion


	#region string or path helper

	static bool IsTextureFile(string _path)
	{
		string path = _path.ToLower();
		return path.EndsWith(".psd") || path.EndsWith(".tga") || path.EndsWith(".png") || path.EndsWith(".jpg") || path.EndsWith(".dds") || path.EndsWith(".bmp") || path.EndsWith(".tif") || path.EndsWith(".gif");
	}

	static string GetRGBTexPath(string _texPath)
	{
		return GetTexPath(_texPath, "_RGB.");
	}

	static string GetAlphaTexPath(string _texPath)
	{
		return GetTexPath(_texPath, "_Alpha.");
	}

	static string GetTexPath(string _texPath, string _texRole)
	{
		string result = _texPath.Replace(".", _texRole);
		string postfix = GetFilePostfix(_texPath);
		return result.Replace(postfix, ".png");
	}

	static string GetRelativeAssetPath(string _fullPath)
	{
		_fullPath = GetRightFormatPath(_fullPath);
		int idx = _fullPath.IndexOf("Assets");
		string assetRelativePath = _fullPath.Substring(idx);
		return assetRelativePath;
	}

	static string GetRightFormatPath(string _path)
	{
		return _path.Replace("\\", "/");
	}

	static string GetFilePostfix(string _filepath)   //including '.' eg ".tga", ".dds"
	{
		string postfix = "";
		int idx = _filepath.LastIndexOf('.');
		if (idx > 0 && idx < _filepath.Length)
			postfix = _filepath.Substring(idx, _filepath.Length - idx);
		return postfix;
	}

	#endregion   
}


这篇文章里有两个明显的问题:
1. 处理Alpha贴图时是一个像素一个像素地处理,用Texture.SetPixel()函数。推荐批量处理,用Texture.SetPixels()函数。推荐批量处理,用Texture.SetPixels()函数。
2. Alpha贴图只存储Alpha通道信息,信息熵较低,理论上可以缩减一些尺寸来存储,但简单地按比例缩小可能会有问题,例如Alpha通道像素相邻一直为黑白黑白的情况,会严重失真,这里的取样需要考虑下。


【改进版】Unity工程里图片的RGB和Alpha通道的分离

http://blog.csdn.net/e295166319/article/details/52624155



你可能感兴趣的:(纹理压缩)