细谈纹理压缩格式

我们先不谈纹理压缩在做什么,我们先看下自然状态下没有压缩的纹理的问题

不压缩的纹理有什么问题

目前的渲染管线中,对于3D物体表面的细节,主要还是靠纹理贴图来表现,分辨率越高,精度越高的纹理,在细节表现上自然越强,但是同时会导致内存开销增大,以及带宽等问题。

占用内存过大

对于RGBA四通道的纹理,每像素需要8bits*4=32bits=4Bytes的空间。使用512X512的贴图,一张图就需要占用1M的内存。假设还开启了Mipmap(多级渐进纹理)还需要额外的1/3大小的空间,也就是1.33M。一个场景如果使用了100张这样的纹理,单独纹理的内存开销就需要133M的内存。如果没有纹理压缩,只能通过降低纹理尺寸来减少内存开销

除了内存还有带宽 (事实上对于移动端游戏带宽相比内存更加紧俏)和磁盘空间

纹理占用内存大,自然也会导致带宽开销变大,与此同时包体大小也会受到影响。而带宽和包体大小对于移动端游戏来讲都是非常稀缺的系统资源,每多用一分都要额外注意,当然内存也是如此。对于需要适配低端设备或者其他部分内存开销很大的游戏,尤其如此。

纹理压缩在做什么

不同于我们通常理解上的图片压缩(png,jpg等),纹理压缩是服务于在3D 图像渲染系统的纹理图片存储技术,因此要充分满足随机访问的特性。

纹理压缩的特性

解压速度 Decoding Speed

为了能够直接从压缩的纹理中直接采样数据,解压速度必须足够快,而且基本上不能影响到渲染性能

随机访问 Random Access

因为预先知道纹理采样的顺序几乎是不可能的,任何纹理压缩技术必须能够快速随机访问解压过的纹理数据。这就和一些常见的图片压缩方法产生冲突,例如JPEG.

压缩率和纹理质量 Compression Rate and Visual Quality

对于渲染来讲,有损压缩是可以接受的。一些纹理压缩库,例如crunch ,允许开发者 灵活的在压缩率和纹理质量见进行平衡。例如Unity的纹理中就支持在选择crunch方式时,设置1-100的纹理质量,数值越低压缩率越高,而纹理的视觉效果也会更差。

压缩速度 Encoding Speed

纹理压缩,对于压缩/解压速率的极度不对称是完全可以容忍的,因为通常纹理压缩只需要在游戏打包时进行一次,对于用户运行时体验完全没有影响。


基于以上特性,大多数纹理压缩算法会把固定大小的像素块的某种形式的固定速率有损矢量量化(lossy vector quantization )包含进固定大小的编码字节块中,有时还会配合一些额外的预处理和后处理步骤。块截断编码(Block Truncation Coding )是这种类型的算法的很常见的一个例子。

因为他们的数据访问模式是明确定义的,纹理解压缩过程是作为图像管线的一部分在运行时进行的,这样可以降低渲染系统对带宽和存储空间的需求。除了纹理贴图,纹理压缩可以应用于其他类型的渲染贴图,包括凹凸贴图和表面法线贴图。同时,纹理压缩可以和其他贴图处理流程共同使用,例如Mip maps和各向异性过滤。

通俗来讲,纹理压缩不同于其他图片压缩方式(jpg,png),在使用中,不会在CPU中进行解压缩,而是直接把压缩内容传给GPU,而且在GPU中也不会一次把整张图片进行解压缩,只会在需要采样特定区域的纹理时对这一区域的纹理进行解压缩。

纹理压缩技术的优点:
  1. 解决了内存和显存(GPU的专用内存)中纹理占用空间大的问题,不需要在CPU中就解压缩,在GPU中也不需要对整个图片解压缩
  2. 解决了带宽问题,从CPU传到GPU的图片是压缩格式,因此传输的数据量会小很多,大大减轻了带宽压力
  3. 解决了包体大小的问题,打包到游戏内的纹理是直接处理好的压缩格式。
纹理压缩的缺点:

因为使用的是有损压缩,3D表面细节会产生一定程度的损失,这属于典型的效果换性能的妥协。


常见的几种纹理压缩格式:

ETC

Unity 支持4种格式的ETC纹理压缩格式,主要应用于安卓设备的OpenGL es 上

  • RGB ETC 4 bit ,每像素占用4bit大小,相比于24bit的RGB格式,压缩比6:1,支持OpenGL es 2 版本
  • RGB ETC2 4 bit ,每像素占用4bit大小,OpenGL es 3.0版本对ETC 格式的兼容。压缩比以及功能和ETC1没有差别,但是压缩质量可能更高(不确定,需要测试)。
  • RGB +1bit Alpha ETC2 4bit ,每像素占用4bit大小。支持1bit的透明通道,也就是只支持镂空图,图片只有透明和不透明部分,没有中间的透明度。
  • RGBA ETC2 8bit ,每像素占用8bit大小,相比于32bit的RGBA格式,压缩比4:1。支持完全的透明通道

对于现代的移动端GPU,几乎都支持OpenGL es 3.x,因此可以根据需要放心的使用所有的ETC格式。

DXT

Unity 支持两种DXT纹理压缩格式,RGB DXT1 和RGBA DXT5。主要应用于DirectX中

  • DXT1 不支持Alpha通道,压缩后每像素占用4bits。因此相比于16bitRGB格式的原始纹理(R6G5B6)压缩比可以达到4:1,相比于24bit的RGB格式(R8G8B8),压缩比可以达到6:1
  • DXT5 支持Alpha通道,压缩后每像素占用8bits。相比于32bitRGBA格式原始纹理(R8G8B8A8),压缩比可以达到4:1。相比于16bitRGBA(R4G4B4A4)只能达到2:1的压缩比。

对于PC平台,大多数RGB格式图片推荐DXT1压缩格式,而对于RGBA格式图片使用DXT5压缩格式。

当目标平台是DX11类型的硬件时(modern PC,PS 4, XboxOne)时,使用BC7可能是更好的选择,因为此时压缩质量会更好。

该格式在Android平台不管是选择OpenGL es3.x或者Vulkan都不支持,实际占用的内存比预期要高

BC6H

  • 对于使用HDR颜色通道的图片(每一个通道需要16-bit)例如高精度天空盒,PBR流程中的环境贴图等,推荐使用这种纹理压缩格式进行压缩,不支持Alpha通道
  • Unity GraphicsFormat的枚举中支持两种BC6H格式:
    • RGB_BC6H_UFloat : 5 指数位 11 尾数位
    • RGB_BC6H_SFloat : 1 符号位+ 5指数位+10 尾数位
  • 如果显卡不支持该格式,在加载时会被解压到RGBAHalf格式(64 bits 每像素)

不同平台的推荐纹理压缩格式

平台 RGB 推荐格式 RGBA 推荐格式 HDR
PC/Console DXT1 DXT5 BC6H
Android ETC 4bit / ETC2 4bit ETC2 8bit
iOS

如果使用了错误的纹理压缩格式会发生什么?

如果使用了硬件不支持的纹理压缩格式,Unity在运行时会对纹理进行解压所到无压缩格式,而这种无压缩格式只是格式上是无压缩的,但是因为原始数据是有损压缩,所以视觉上纹理精度和压缩格式是一致的,因此单从视觉上很难察觉,而且占用的内存是双份纹理的开销,而带宽开销是无压缩格式的纹理大小所占用的开销。

例如:ETC格式在PC端Dx11的显卡是不支持的,如果使用,开销计算如下

  1. 1024*1024 ETC2 8bit的格式在PC端,会产生5M的内存开销(1M压缩的原图+4M解压出来的RGBA32bit图)
  2. 1024*1024 ETC 4bit格式在PC端,会产生4.5M的内存开销(0.5M压缩的原图+4M解压出来的RGBA32bit图)

而如果贴图格式在目标设备上支持,则不会发生内存中的解压缩操作,压缩过的图大小是多少,在内存中的设备上大小就是多少:

  1. 1024*1024 ETC2 8bit的格式在Android端,会产生1M的内存开销(1M压缩的原图)
  2. 1024*1024 ETC 4bit格式在Android端,会产生0.5M的内存开销(0.5M压缩的原图)

对于移动平台游戏,如果勾选了Unity中的 贴图设置中的Override for Android ,一定要注意,除非你特别清楚你的贴图在目标设备上的兼容情况,否则不要使用ETC格式以外的纹理压缩格式,否则造成的额外内存开销是巨大的。

iOS这里因为没有在真机测试,因此没有做说明,将在另一篇文章单独讨论iOS设备上的纹理压缩格式。

你可能感兴趣的:(unity,图形学,渲染)