为什么图片导入Unity之后,默认会设置为压缩格式?
在glTexImage2D这个接口里面,设置图片数据保存为压缩格式后,会有什么好处?
/** * @brief 将图片数据上传到GPU;* @param target 目标纹理,GL_TEXTURE_2D(2D纹理)* @param level 当图片数据是包含多个mipmap层级时,指定使用mipmap层级。* @param internalformat 图片数据上传到GPU后,在显存中保存为哪种格式?* @param width* @param height* @param border* @param format 上传的图片数据格式,RGB、RGBA、Alpha等* @param type 图片数据变量格式,一般都是GL_UNSIGNED_BYTE(0-255范围)* @param pixels 图片数据* @return*/
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels);
答案就是:节省显存。
如今的3A大作,各种4K贴图、超高精度模型,一个场景数据量几G,这些数据都是要上传到显存的,为了让游戏适配更多的硬件,开发者们也是各显神通,压缩纹理就是OpenGL官方提供的一种手段。
比如:之前glTexImage2D这个接口的 internalformat 参数值是GL_RGB,
//3. 将图片rgb数据上传到GPU;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture2d->width_, texture2d->height_, 0, texture2d->gl_texture_format_, GL_UNSIGNED_BYTE, data);
现在只要设置为对应的压缩纹理格式GL_COMPRESSED_RGB即可。
//3. 将图片rgb数据上传到GPU;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture2d->width_, texture2d->height_, 0, texture2d->gl_texture_format_, GL_UNSIGNED_BYTE, data);
代码编译运行后,正常绘制了立方体,但是怎么样确认压缩纹理的效果呢,就是说怎么样确认显存占用降低?
这里借助GPU状态工具 - GPU-Z 来查看实时显存。
使用未压缩纹理,glTexImage2D接口调用前后显存对比如下:
从52m 变动到 116m,使用了约 64m 显存。
使用压缩纹理,接口调用前后显存对比如下:

从52m 变动到 60m,使用了约 8m 显存。
效果很明显,压缩纹理后,占用的显存是原来的 1/6 左右。
具体查看5.6 压缩纹理
将压缩好的纹理数据,保存到硬盘作为图片文件。
带来的好处是:
压缩纹理数据已经从GPU下载到内存,并保存为.cpt文件,cpt 是我取自compressed texture的缩写。
具体查看5.7 图片压缩工具
带来的性能提升,如下所示:
| 解析耗时(ms) | 上传耗时(ms) | |
|---|---|---|
| jpg | 1571 | 960 |
| cpt | 4 | 16 |
有很大的性能提升。
回顾一下,我们是先将.jpg图片,解析得到RGB数据,调用OpenGL API进行压缩上传至GPU,然后再从GPU下载压缩纹理数据,保存为.cpt文件。
这其实就是Unity导入图片的时候干的活,现在你知道为什么Unity导入图片那么慢了!
具体查看 5.8 使用压缩纹理
截图的效果可以参考下面几篇博客:
纹理压缩(Texture compression)是一种专为在三维计算机图形渲染系统中存储纹理而使用的图像压缩技术。与普通图像压缩算法的不同之处在于,纹理压缩算法为纹素的随机存取做了优化。
在实际应用特别是游戏中纹理占用了相当大的包体积,而且GPU无法直接解码目前流行的图片格式,图片必须转换为RGB等类型的格式才能上传到GPU内存,这显然增加了GPU内存的占用。为了处理这些问题于是出现了GPU支持的压缩纹理格式,在GPU中进行解码。压缩纹理属于有损压缩,更在意解码速度,而编码在程序运行之前,因此速度较慢。

glGetString(GL_RENDERER)
glGetString(GL_VENDOR);
string extensions = (const char*)glGetString(GL_EXTENSIONS);
return (extensions.find("GL_OES_compressed_ETC1_RGB8_texture")!= string::npos);
return (extensions.find("GL_EXT_texture_compression_dxt1")!= string::npos ||extensions.find("GL_EXT_texture_compression_s3tc")!= string::npos);
return (extensions.find("GL_IMG_texture_compression_pvrtc")!= string::npos);
return (extensions.find("GL_AMD_compressed_ATC_texture")!= string::npos ||extensions.find("GL_ATI_texture_compression_atitc")!= string::npos);
void glCompressedTexImage2D(GLenum target,GLint level,GLenum internalformat,GLsizei width,GLsizei height,GLint border,GLsizei imageSize,const GLvoid * data);
internalformat即是压缩纹理格式的类型。
int num_formats;
glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &num_formats);
std::cout<<"Texture extensions: "<std::cout<
GL_COMPRESSED_RGB : GL_RGB
GL_COMPRESSED_RGBA : GL_RGBA
GL_COMPRESSED_SRGB : GL_SRGB
GL_COMPRESSED_SRGB_ALPHA : GL_RGBA
GL_COMPRESSED_RED : GL_RED
GL_COMPRESSED_RG : GL_RG

除了这些压缩格式外,OpenGL中还加入了一些特定的压缩格式,即
GL_COMPRESSED_SIGNED_RED_RGTC1GL_COMPRESSED_SIGNED_RED_RGTC2GL_COMPRESSED_SIGNED_RG_RGTC2它们用于各种单颜色通道和双颜色通道压缩纹理,他们代替了兼容版本中GL_LUMINANCE和GL_LUMINANCE_ALPHA的功能
//判断纹理是否被成功压缩
GLint comFlag;
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_COMPRESSED,&comFlag);//根据选择的压缩纹理格式,
//选择最快、最优、⾃行选择的算法⽅式选择压缩格式。
glHint(GL_TEXTURE_COMPRESSION_HINT,GL_FASTEST); //最快
glHint(GL_TEXTURE_COMPRESSION_HINT,GL_NICEST); //质量最好
glHint(GL_TEXTURE_COMPRESSION_HINT,GL_DONT_CARE); //自行选择
参数说明:
GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D。format、type、data参数:与我们在讲glDrawPixels函数对应的参数相同
glGetTexLevelParameter函数提取的压缩纹理格式如下:

GL_TEXTURE_COMPRESSED:如果纹理被压缩返回1,否则返回0
GL_TEXTURE_COMPRESSED_IMAGE_SIZE:获取压缩后的纹理大小(以字节为单位)
GL_TEXTURE_INTERNAL_FORMAT:所使用的压缩格式
GL_NUM_COMPRESSED_TEXTURE_FORMATS:支持的压缩纹理格式数量
GL_COMPRESSED_TEXTURE_FORMATS:支持的压缩纹理格式数组
GL_TEXTURE_COMPRESSION_HINT: 选择压缩格式的方式
GL_EXT_texture_compression_s3tc压缩格式如下:
基于OpenGL ES的压缩纹理有常见的如下几种实现:
ETC1格式是OpenGL ES图形标准的一部分,并且被所有的Android设备所支持。
扩展名为: GL_OES_compressed_ETC1_RGB8_texture,不支持透明通道,所以仅能用于不透明纹理。 且要求大小是2次幂。
当加载压缩纹理时,参数支持如下格式: GL_ETC1_RGB8_OES(RGB,每个像素0.5个字节)
ETC2 是 ETC1 的扩展,压缩比率一样,但压缩质量更高,而且支持透明通道,能完整存储 RGBA 信息。
ETC2 需要 OpenGL ES 3.0(对应 WebGL 2.0)环境,目前还有不少低端 Android 手机不兼容,iOS 方面从 iPhone5S 开始都支持 OpenGL ES 3.0。
ETC2 和 ETC1 一样,长宽可以不相等,但要求是 2 的幂次方。
支持的GPU为Imagination Technologies的PowerVR SGX系列。
OpenGL ES的扩展名为: GL_IMG_texture_compression_pvrtc。
当加载压缩纹理时,参数支持如下几种格式:
GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG (RGB,每个像素0.5个字节)GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG (RGB,每个像素0.25个字节)GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG (RGBA,每个像素0.5个字节)GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG (RGBA,每个像素0.25个字节)支持的GPU为Qualcomm的Adreno系列。
支持的OpenGL ES扩展名为: GL_ATI_texture_compression_atitc。
当加载压缩纹理时,参数支持如下类型的纹理:
GL_ATC_RGB_AMD (RGB,每个像素0.5个字节)GL_ATC_RGBA_EXPLICIT_ALPHA_AMD (RGBA,每个像素1个字节)GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD (RGBA,每个像素1个字节)也被称为DXTC,在PC上广泛被使用,但是在移动设备上还是属于新鲜事物。支持的GPU为NVIDIA Tegra系列。
OpenGL ES扩展名为: GL_EXT_texture_compression_dxt1和GL_EXT_texture_compression_s3tc。
当加载压缩纹理时,参数有如下几种格式:
GL_COMPRESSED_RGB_S3TC_DXT1 (RGB,每个像素0.5个字节)GL_COMPRESSED_RGBA_S3TC_DXT1 (RGBA,每个像素0.5个字节)GL_COMPRESSED_RGBA_S3TC_DXT3 (RGBA,每个像素1个字节)GL_COMPRESSED_RGBA_S3TC_DXT5 (RGBA,每个像素1个字节)每种压缩纹理以及相应的厂商都提供了压缩纹理的工具,包括可视化工具和命令行工具,可自行下载
Imagination Technologies PowerVR
PVETextTool
Qualcomm Adreno
Adreno Texture Tool
ARM Mali
Mail Texture Compression Tool
nVIDIA Tegra
DirectX Texture Tool
下一篇:量子计算(十):量子计算原理