之前我们介绍了点聚合图的绘制方案:潘与其:使用 k-d tree 实现点聚合图zhuanlan.zhihu.com
对于每个圆形我们使用了如下的顶点数据:
attribute vec2 a_pos; // 瓦片坐标
attribute float a_radius; // 半径
attribute vec2 a_extrude; // 拉伸后的点阵坐标
attribute vec4 a_color; // 颜色
随着特性的增加,后续需要存储的顶点数据类型也会增多。例如后续增加基于要素的拾取,就需要存储 pickingId。
如果我们能尽量利用 vec4 存储这些顶点数据并采用一定的压缩技术,无疑能减少 CPU 侧向 GPU 侧传递数据的时间并节省大量 GPU 内存。另外,OpenGL 支持的 attribute 数目是有上限的,当然我们这个简单 DEMO 并不会超出。
本文会依次介绍以下内容:压缩颜色、半径以及点阵坐标数据
使用 Chrome MemoryInfra 度量 GPU 内存,对比优化前后效果
Cesium、Mapbox 中的实践,包括对于其他类型数据的压缩方案
压缩方案
首先以下的压缩方案都是需要在 CPU 侧 JS 中压缩,在 vertex shader 中解压。因此必然会牺牲一定运行时性能,但是在地理信息海量要素展示的场景下,换取的 GPU 内存收益是很客观的。
对于颜色数据每个分量其实只需要 8 bits 就够了,因此一个 16-bit float 就可以存储两个分量,这样就只需要 vec2 存储颜色数据,JS 中压缩方法如下:
function packUint8ToFloat(a: number, b: number) {
a = clamp(Math.floor(a), 0, 255);
b = clamp(Math.floor(b), 0, 255);
return 256 * a + b;
}
// vec2packUint8ToFloat(r, g);
packUint8ToFloat(b, a);
相应的,在 vertex shader 中进行解压:
vec2 unpack_float(const float packedValue) {
int packedIntValue = int(packedValue);