压缩纹理实践

154 阅读5分钟

0x01

本文将讲述压缩纹理在实际项目中的使用的案例。最近的一个项目是这样的:项目由于涉及到的建筑物特别多,大概有近40栋的建筑,而每一栋建筑物,又有10层楼,每层楼里面又有很多的设备。这就导致我们需要使用到大量的贴图。在实际的项目过程中,我们的客户的电脑会经常遇到webgl崩溃的情况。这就需要我们想办法来减少该项目下贴图显存和内存的占用。

下面是园区和楼层的部分截图,数据已脱敏:

数据已脱敏

数据已脱敏

首先我们可能会想到的是减少图片的尺寸。但是减少图片尺寸也是有限度的,因为图片尺寸太小的话,会影响我们最终的呈现效果。因此我们就需要想新的办法来减少这种资源的占用。

另外一种思路就是 不使用generateMipmap。 Mipmap会生成金字塔式的图片结果。生成Mipmap会多占有大概1/3的内存空间,因此不使用Mipmap会减少啊,大概1/3的内存空间。但是Mipmap是为了模型在缩小的时候能够有很好的呈现效果,因此不使用Mipmap呢,就会导致呈现质量的下降。

最终我们使用压缩纹理。

0x01 压缩纹理简介

相信对于webgl比较熟悉的同学都知道压缩纹理。

我们常用的的纹理图片,都是jpeg,png等图片格式。 这些图片本事是压缩的图片,基于压缩算法,对于文件进行了压缩,减小了文件的大小,这对于网络上的大量的图片的传输是有利的。

不过jpeg png的图片在作为贴图使用的时候,首先会转换成位图,这里所说的位图是指没有使用任何压缩算法的原始图片数据。以1024×1024为例,如果图像中每个像素需要RGB三个通道,每个通道需要8位空间,那么整张图片就需要使用1024 x 1024 x 8 x 3 位的信息,也就是3M,这3M的信息都需要加载到GPU当中,这和图片文件采用什么样的压缩格式没有任何关系。如果图像中每个像素需要rgba 四个通道,则是则需要4兆的GPU内存空间。

可以看出使用jpeg, png等图片格式的缺点是:

  1. 图片需要解压,这会消耗CPU的性能。
  2. 纹理数据占用内存较多。通常是浏览器和GPU各自保存一份位图数据。

压缩纹理正是为了解决上述的问题而出现的。通过使用压缩纹理,我们可以把像素通过压缩算法,包装成数据块,这可以减少在显存中的存储容量。 这种包装好的数据块又很方便GPU进行解压,查询。所以从性能的角度来说也提高了访问纹理的速度。

压缩文里有很多格式,比如DDS,KTX等。严格来说,DDS和kTX是一种容器而不是格式,而压缩纹理的格式是有多种。此处为了简便起见,我们就说成DDS和KTX格式,我们知道KTX有2.0的版本。而我们最终选用的也就是ktx2.0,它能够很方便的和gltf模型格式进行集成。

有关压缩纹理的更多知识,大家可以在网上搜索啊,此处不进行详细的介绍。

0x02 工作流

建模工程师给的是OBJ模型,项目最开始用的也是OBJ模型,首先我们需要把OBJ模型转换成GLTF格式。 可以使用插件obj2gltf进行转换。转换的流程大致如下:

npm install obj2gltf -g
obj2gltf -i a.obj -o a.gltf

首先通过npm安装obj2gltf。然后通过obj2gltf 进行模型的转换,其中 -i表示输入的OBJ模型。-o就是输出的gltf模型。

转换为gltf之后,通过对gltf进行压缩。其中贴图压缩的工具为一套开源的ktx工具,ktx-software,官方文档如下: github.com/KhronosGrou…

安装ktx-software之后,可以对贴图进行压缩,此处我们可以选择gltfpack(gltfpack相对比较简单,ktx-software也自带转换工具,比较复杂,但是转换的可选参数更多,能够更灵活的选择高质量还是低质量压缩纹理,有兴趣读者可以查看文档研究)。 压缩的命令大致:

gltfpack -i scene.gltf -o scene.glb --tc

其中tc是对贴图进行压缩,原文如下:gltfpack can also compress textures using Basis Universal format stored in a KTX2 container (-tc flag, requires support for KHR_texture_basisu). Textures can also be embedded into .bin/.glb output using -te flag.

0x03加载压缩贴图

贴图改成ktx2之后,在加载模型的时候需要对贴图进行解析,以threejs为例,加载ktx2需要指定KTX2Loader,KTX2Loader需要指定js文件“basis_transcoder”,所以需要指定basis_transcoder.js和basis_transcoder.wasm文件的路径,大致如下所示:

let ktx2Loader = new KTX2Loader(manager)
  .setTranscoderPath('./')
  .detectSupport(renderder)
gltfLoader.setKTX2Loader(ktx2Loader);

gltfLoader指定ktx2loader之后,就可以像加载普通gltf模型一样,加载带ktx2贴图的模型。

0x04 压缩结果对比

压缩之后,webgl程序的显存和内存占用都大幅度的降低。 整个场景的内存下降大概50%左右,效果还是很明显的。 当然降低显存,还有许多更多的手段,比如建模过程中降低模型的面数,减小贴图的尺寸,禁用mipmap,能复用的模型尽量复用等等。

结语

本文讲述了降低显存的一种手段,压缩纹理。 压缩纹理和性能优化还有更多的知识技能点,如果你有好的经验,也欢迎和我交流。关注公号“ITMan彪叔” 可以添加作者微信进行交流,及时收到更多有价值的文章。