Contents

如何进行贴图的实时压缩

Contents

让我们暂时忽略具体的压缩算法实现,只考虑一个最基本的问题,如何让GPU生成一张压缩了的贴图。既然我们需要一张压缩了的贴图,那么首先,就应该以指定的压缩格式创建出一幅贴图,这没什么问题。然而遗憾的是,无论是pixel shader还是compute shader,通常都无法将数据写入压缩格式的贴图。

一种变通的方法是,第一步先把数据写入shader直接支持写入操作的格式的贴图中,如R32G32B32A32_UINT的贴图中,然后第二步进行数据类型的转换。

针对第一步,目前主流的GPU,都支持在shader中操作和写入整数类型的数据,所以在shader中按压缩格式编码贴图没有任何问题。

针对第二步,目前主流的API都提供了转换贴图格式的接口或方法,分别是:

  1. Windows:DX10.1及以上,使用CopyResource用一次拷贝转换格式
  2. Android:ES3.2及以上(Vulkan),使用CopyImageSubData用一次拷贝转换格式
  3. IOS:支持Metal就行,从MTLBuffer中创建MTLTexture作为RT,然后将MTLBuffer中的内容用copy复制到压缩格式的MTLTexture中

对于D3D12、Vulkan、Metal这些新一代API,应该可以用资源aliasing避免拷贝,还没有尝试 以上这些功能类似与C++中的reinterpret_cast,可以在保留数据的内容的前提下,按另一种格式来解释数据的内容,间接实现了在shader中写入压缩格式的贴图。

压缩的类型

贴图的内存的排布可以有多种形式,可以按照压缩的和非压缩的两大类。 我们当前举的是非压缩中的一种内存布局,按照这种内存布局把每个像素对应的数据填充到GPU纹理绑定的内存上。

GPU应用的数据是按照一定内存布局排布的非压缩或者压缩数据。那么美术同学制作的图片文件资源是不是就按照相应的格式存储的呢?一般我们美术同学会把贴图存储为PNG或者TGA格式。这是两种比较常用的压缩格式,和上文所说的GPU纹理所应用的压缩格式不同,应用的是流压缩方法。

GPU应用的压缩方式是块压缩模式。流压缩模式和块压缩模式不同的是:解压图片需要整个图片文件的完整压缩数据,块压缩不需要整个图片的压缩数据,只需要相关的分块即可。如果引擎用PNG,TGA格式的图片文件进行渲染,需要用CPU把图片文件软解为非压缩位图格式存储于内存中,然后把该位图内存绑定到GPU的纹理上。

因此PNG,TGA对于内存节省是没有益处的,反而因为CPU软解增加了计算开销,他们存在的意义是节省硬盘存储空间或者网络传输数据量。PNG,TGA不是针对GPU和渲染效率来设计的压缩格式,他们有其历史价值,在网络传输中大大减少了数据量。我们美术同学制作的资源是PNG, TGA,往往是因为很多美术相关软件例如PhotoShop更好的支持这两种格式的输出。

游戏业界中公认的压缩比和压缩质量很好的一种压缩类型是ASTC。ASTC是块压缩方式,它可以直接读入内存中不用CPU软解,直接绑定到GPU纹理上(大多数iOS和Android手机都支持ASTC压缩纹理)。因此ASTC在加载效率和内存空间占用都是比较优秀的。