在unity開發(fā)過程中疾捍,游戲圖片占用了很大一部分的手機內(nèi)存。所以在游戲開發(fā)中栏妖,對圖片的優(yōu)化也至關(guān)重要乱豆。
在Unity中常用的的圖片格式有RGBA32,RGBA16底哥,ETC咙鞍,PVRTC等房官。這里我們主要討論帶透明通道的RGBA32和RGBA16這兩種格式的圖片在Unity占用的內(nèi)存空間已經(jīng)優(yōu)化方案趾徽。
我們知道,RGBA32是R,G,B,A四個通道每個通道用8位來表示翰守,RGBA16則是用4位孵奶。所以RGBA32能夠帶來更好的顯示效果。同時也會帶來更大的內(nèi)存消耗蜡峰。下面給兩張RGBA16和RGBA32的對比圖了袁。
RGBA32
RGBA32.png
RGBA16
RGBA16.png
從上面兩個圖可以看得出朗恳,RGBA32能夠帶來更好的顯示效果。而RGBA16在有些地方的色階太明顯载绿,導(dǎo)致顯示效果不盡人意粥诫。由此keijiro(Github地址)寫了一個dither算法來消除這種色階,以達到高于RGBA16低于RGBA32的顯示效果崭庸。
下圖為Dither優(yōu)化之后的RGBA16
RGBA16-Dither.png
通過對比能明顯看出優(yōu)化后的RGBA16能夠消除色階怀浆,如果不是放大特意看,和RGBA32幾乎差別不同怕享。通過這種方式將內(nèi)存降低一半確認帶來更好的效果执赡。
萬事總會存在利弊,這個算法在消除了色階的同事函筋,帶來的是更多的噪點沙合。所以這個方法不適用于圖片需要放大來顯示的〉剩總體來說首懈,該方案在一定程度上還是能夠帶來很好的效果。
最后把算法的核心代碼貼出來 谨敛。
public class TextureImportSetting : AssetPostprocessor {
string[] assetTexturePath = new string[]{"Assets/Resources/TextureVN/"}; //放置需要優(yōu)化的路徑
void OnPreprocessTexture(){
foreach (var str in assetTexturePath)
{
if (this.assetPath.StartsWith(str) )
{
TextureImporter textureImporter = this.assetImporter as TextureImporter;
textureImporter.textureType = TextureImporterType.Advanced;
textureImporter.npotScale = TextureImporterNPOTScale.ToNearest;
textureImporter.mipmapEnabled = false;
textureImporter.isReadable = false;
if ( textureImporter.DoesSourceTextureHaveAlpha())
{
textureImporter.textureFormat = TextureImporterFormat.RGBA32;
}
}
}
}
public static void OnPostprocessRGB16 (Texture2D texture)
{
var texw = texture.width;
var texh = texture.height;
var pixels = texture.GetPixels ();
var offs = 0;
var k1Per15 = 1.0f / 15.0f;
var k1Per16 = 1.0f / 16.0f;
var k3Per16 = 3.0f / 16.0f;
var k5Per16 = 5.0f / 16.0f;
var k7Per16 = 7.0f / 16.0f;
for (var y = 0; y < texh; y++) {
for (var x = 0; x < texw; x++) {
float a = pixels [offs].a;
float r = pixels [offs].r;
float g = pixels [offs].g;
float b = pixels [offs].b;
var a2 = Mathf.Clamp01 (Mathf.Floor (a * 16) * k1Per15);
var r2 = Mathf.Clamp01 (Mathf.Floor (r * 16) * k1Per15);
var g2 = Mathf.Clamp01 (Mathf.Floor (g * 16) * k1Per15);
var b2 = Mathf.Clamp01 (Mathf.Floor (b * 16) * k1Per15);
var ae = a - a2;
var re = r - r2;
var ge = g - g2;
var be = b - b2;
pixels [offs].a = a2;
pixels [offs].r = r2;
pixels [offs].g = g2;
pixels [offs].b = b2;
var n1 = offs + 1;
var n2 = offs + texw - 1;
var n3 = offs + texw;
var n4 = offs + texw + 1;
if (x < texw - 1) {
pixels [n1].a += ae * k7Per16;
pixels [n1].r += re * k7Per16;
pixels [n1].g += ge * k7Per16;
pixels [n1].b += be * k7Per16;
}
if (y < texh - 1) {
pixels [n3].a += ae * k5Per16;
pixels [n3].r += re * k5Per16;
pixels [n3].g += ge * k5Per16;
pixels [n3].b += be * k5Per16;
if (x > 0) {
pixels [n2].a += ae * k3Per16;
pixels [n2].r += re * k3Per16;
pixels [n2].g += ge * k3Per16;
pixels [n2].b += be * k3Per16;
}
if (x < texw - 1) {
pixels [n4].a += ae * k1Per16;
pixels [n4].r += re * k1Per16;
pixels [n4].g += ge * k1Per16;
pixels [n4].b += be * k1Per16;
}
}
offs++;
}
}
texture.SetPixels (pixels);
EditorUtility.CompressTexture (texture, TextureFormat.RGBA4444, TextureCompressionQuality.Best);
}
void OnPostprocessTexture(Texture2D texture){
foreach (var str in assetTexturePath)
{
if (this.assetPath.StartsWith(str) )
{
TextureImporter textureImporter = this.assetImporter as TextureImporter;
if ( textureImporter.DoesSourceTextureHaveAlpha())
{
OnPostprocessRGB16(texture);
}
}
}
}
}