贴图内存优化

unity移动端贴图压缩格式

格式 通道 适用平台 压缩率1 bits/pixel
RGB Compressed PVRTC 2 bits RGB Android(PowerVR),iOS 8.33% 2
RGBA Compressed PVRTC 2 bits RGBA Android(PowerVR),iOS 6.25% 2
RGB Compressed PVRTC 4 bits RGB Android(PowerVR),iOS 16.67% 4
RGBA Compressed PVRTC 4 bits RGBA Android(PowerVR),iOS 12.5% 4
RGB Compressed ETC 4 bits RGB Android,iOS 16.67% 4
RGB Compressed ETC2 4 bits RGB Android(OpenGL ES 3.0),iOS(OpenGL ES 3.0) 16.67% 4
RGB + 1-bit Alpha Compressed ETC2 4 bits RGBA Android(OpenGl ES 3.0),iOS(OpenGL ES 3.0) 12.5% 4
RGBA Compressed ETC2 8 bits RGBA Android(OpenGL ES 3.0),iOS(OpenGL ES 3.0) 25% 8
R Compressed EAC 4 bits R Android(OpenGL ES 3.0),iOS(OpenGL ES 3.0) 50% 4
RG Compressed EAC 8 bits RG Android(OpenGL ES 3.0),iOS(OpenGL ES 3.0) 50% 8
RGB Compressed ASTC 4x4 block RGB Android,iOS(A8) 33.33% 8
RGB Compressed ASTC 5x5 block RGB Android,iOS(A8) 21.33% 5.12
RGB Compressed ASTC 6x6 block RGB Android,iOS(A8) 14.81% 3.56
RGB Compressed ASTC 8x8 block RGB Android,iOS(A8) 8.33% 2
RGB Compressed ASTC 10x10 block RGB Android,iOS(A8) 5.33% 1.28
RGB Compressed ASTC 12x12 block RGB Android,iOS(A8) 3.71% 0.89
RGBA Compressed ASTC 4x4 block RGBA Android,iOS(A8) 25% 8
RGBA Compressed ASTC 5x5 block RGBA Android,iOS(A8) 16% 5.12
RGBA Compressed ASTC 6x6 block RGBA Android,iOS(A8) 11.11% 3.56
RGBA Compressed ASTC 8x8 block RGBA Android,iOS(A8) 6.25% 2
RGBA Compressed ASTC 10x10 block RGBA Android,iOS(A8) 4% 1.28
RGBA Compressed ASTC 12x12 block RGBA Android,iOS(A8) 2.78% 0.89

1:对比RGB24 或 RGBA32

unity自动做的优化

  • normal map会被压缩成两个通道

unity提供的分析接口

  • 文件大小
    • UnityEditor.TextureUtil.GetStorageMemorySizeLong(Texture)
  • 占用内存大小
    • UnityEditor.TextureUtil.GetRuntimeMemorySizeLong(Texture)
    • UnityEngine.Profiling.Profiler.GetRuntimeMemorySizeLong(Texture)

其中查看内存大小的接口在Editor下得到的数据会偏大,因为Editor下会创建一份贴图的副本。
如果贴图有生成mipmap,那么这份副本会从mipmap中拷贝,否则副本的分辨率等于贴图原始分辨率(别问我是怎么知道的)。
要在Editor下获得贴图的运行时内存占用,比较准确的方法是使用GetStorageMemorySizeLong方法2,再根据贴图的可读写标记决定是否乘以系数2。

2:在不开启Read/Write的情况下,GetStorageMemorySizeLong方法得到的大小和实际内存占用几乎没有区别。

优化贴图大小

unity的TextureImporter可以很方便地调整贴图的目标大小,打包时会自动根据ImportSetting对贴图进行压缩。
但修改ImportSeting并不会修改原始图片大小,大量高清资源不但会降低项目加载速度,还会增加版本控制的开销。
可以使用python的PIL库对贴图批量scale down,自动缩放到对应ImportSetting的分辨率

Install Requirements:

1
pip install pyyaml pillow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from PIL import Image
import os
import sys
import getopt
import yaml
import re


def find_files(directory, pattern_filename, pattern_ext):
for dirpath, subdir, filenames in os.walk(directory):
for filename in filenames:
name, ext = os.path.splitext(filename)
if re.search(pattern_ext, ext) and re.search(pattern_filename, name):
yield os.path.join(dirpath, filename)


def resize(filename, factor=1, filtermode=Image.BILINEAR):
factor = max(factor, 1)
metafile = open(filename + '.meta', mode='r')
meta = yaml.load(metafile, Loader=yaml.FullLoader)
metafile.close()
maxsize = meta['TextureImporter']['platformSettings'][0]['maxTextureSize']
img = Image.open(filename)
img.load()
ratio = float(maxsize * factor) / max(img.width, img.height)
if ratio > 1.5:
return
size = int(img.width * ratio), int(img.height * ratio)
bands = img.split()
bands = [b.resize(size, filtermode) for b in bands]
resized = Image.merge(img.mode, bands)
resized.save(filename)


pattern_ext = r'^\.((?i)(bmp|exr|gif|hdr|iff|jpg|pict|png|psd|tga|tiff))$'

pattern = '.*'
path = '.'
filtermode = Image.BILINEAR
factor = 1.0

options = '-h-p:-d:-f:-s:'
long_options = ['help', 'pattern=', 'dir=', 'filter=', 'size=']
usage = r'''Usage: resizer.py [-h] [-dpfs] <value>
resize.py [--help] [--dir|--pattern|--filter|--size]=<value>

-h, --help : Print this message.
-d, --dir : Directory.
-p, --pattern: Regex. (Default '.*')
-f, --filter : NEAREST, BILINEAR, BICUBIC, LANCZOS. (Default BILINEAR)
-s, --size : Final texture size = maxTextureSize * size. (Default 1.0)'''

try:
opts, args = getopt.getopt(sys.argv[1:], options, long_options)

except getopt.GetoptError:
print(usage)
sys.exit()

for opt, arg in opts:
if opt in ("-h", "--help"):
print(usage)
sys.exit()
elif opt in ("-p", "--pattern"):
pattern = arg
elif opt in ("-d", "--dir"):
path = arg
elif opt in ("-f", "--filter"):
if arg in ("NEAREST", "nearest"):
filtermode = Image.NEAREST
elif arg in ("BILINEAR", "bilinear"):
filtermode = Image.BILINEAR
elif arg in ("BICUBIC", "bicubic"):
filtermode = Image.BICUBIC
elif arg in ("LANCZOS", "lanczos"):
filtermode = Image.LANCZOS
elif opt in ("-s", "--size"):
factor = float(arg)

for filename in find_files(path, pattern, pattern_ext):
print(filename)
resize(filename, factor, filtermode)