为什么要用压缩纹理
下面这张图是一辆陆虎越野车模型所用的纹理,原始分辨率为1024 x 1024。
浏览器从服务端加载这样一张图片时,其格式通常为JPEG,文件尺寸只有166KB,但是当WebGL处理一张纹理时就需要按照位图处理(这里所说的位图是指没有使用任何压缩算法的原始图片数据),如果图像中每个像素需要RGB三个通道,每个通道需要8位空间,那么整张图片就需要使用1024 x 1024 x 8 x 3 位的信息,也就是3M,这3M的信息都需要加载到GPU缓存当中,这和图片文件采用什么样的压缩格式没有任何关系。当调用gl.texImage2D方法时,浏览器内部就会将图片文件进行解压,转换成位图格式。如果图片包含透明信息,那么RGBA格式那么还要额外增加内存使用。
在简单了解WebGL处理纹理的过程后,会知道使用JPEG或者PNG文件作为纹理时会有这样的问题:
需要有图片解压过程,比较耗时。
因为纹理数据较大,所以传输纹理数据耗时较多。
纹理数据占用内存较多。通常是浏览器和GPU各自保存一份位图数据。
压缩纹理的出现就是来解决这些问题,经过某种算法压缩之后的纹理可直接被GPU使用,只有当shader进行纹理查询(texture lookup)才会进行解压操作,找到对应位置的像素颜色。GPU通常会对解压过程进行优化从而提升性能。
浏览器的图片缓存策略为了更好的了解WebGL处理纹理时如何分配和使用内存,请看下面的实验。
首先,页面加载前文提到的陆虎车的纹理,打开Chrome的任务管理器(Task Manager),把 Image Cache 和 GPU Memory 这两项勾选,查看图片缓存和GPU内存用量。你可以打开这个demo自行观察。
这时 Image Cache 显示:4262K,GPU Memory 显示:17.7M。
现在,我们注释掉加载纹理的语句,这时候图片不会加载,纹理也不会被绘制出来。
此时 Image Cache 显示:0K,GPU Memory 显示:13.7M。
如果加载图片,但是不创建纹理会如何?
结果是 Image Cache显示:166K,GPU Memory显示:13.7M。
通过以上观察可以推出以下结论。
当仅下载图片时 Image Cache 以图片原始格式进行缓存,当创建纹理时,浏览器会解压成为位图格式,并将位图数据进行缓存。
GPU Memory 以位图格式保存纹理数据。
通过Chrome的Profile工具可以看到在JavaScript层面,其内存消耗只包含HTMLImageElement对象所占用的内存大小。
以上结论可以用下面这张图来概括:
当使用压缩纹理时,内存使用状况变为:
压缩纹理的种类不同的GPU厂商会有不同的纹理压缩格式,具体如下:
S3TC/DXTn/BCn:桌面计算机常见的压缩格式。名字虽然有不同叫法,但都是指同一种压缩方式。通常以DDS文件格式保存。
PVRTC/PVRTC2:iOS设备上使用的压缩纹式。
ETC/ETC2:随着OpenGL ES 2.0 出现。
ASTC:2012年出现的一种新压缩格式。
ATC:Adreno GPU支持的一种压缩格式,Android手机上常用。
压缩纹理支持情况
WebGL1.0里,压缩纹理是通过扩展支持的,因此要看当前浏览器支持哪些扩展。具体判断方法如下:
var availableExtensions = gl.getSupportedExtensions(); for (var i = 0; i < availableExtensions.length; i++) { if (availableExtensions[i].indexOf(\'texture\') >= 0 && availableExtensions[i].indexOf(\'compressed\') >= 0) { // show in console console.log(availableExtensions[i]); } }