之前我们介绍了点聚合图的绘制方案:潘与其:使用 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);

int v0 = packedIntValue / 256;

return vec2(v0, packedIntValue - v0 * 256);

}

vec4 decode_color(const vec2 encodedColor) {

return vec4(

unpack_float(encodedColor[0]) / 255.0,

unpack_float(encodedColor[1]) / 255.0

);

}

这样我们就只需要一个 vec4 存储瓦片坐标和颜色数据:

attribute vec4 a_pos_color; // 瓦片坐标 + 颜色

vec2 tile_pos = a_pos_color.xy;

vec2 color = a_pos_color.zw;

接下来我们还有点阵坐标(vec2)和半径(float),有没有可能只使用一个 float 存储它们呢?

GLSL 中 float 是单精度浮点数binary32 bits layout

利用好这 24 bits 的精度,我们完全可以将一些特殊类型的数据(int、bool)压缩进来。

例如我们的场景中,点阵坐标 xy 取值只有 -1 和 1,完全可以 + 1 之后(0,2)使用 2 bits 存储。这样每个点阵坐标只需要 4 bits,完全没必要使用 vec2。另外 radius 也不会特别大,16 bits 应该也够用了。因此利用简单的乘法进行位移:

const LEFT_SHIFT18 = 262144.0;

const LEFT_SHIFT20 = 1048576.0;

(extrude[0] + 1) * LEFT_SHIFT20

+ (extrude[1] + 1) * LEFT_SHIFT18

+ radius, // 16 bits

在 shader 中 decode 时也要注意和 encode 顺序保持一致:

attribute float a_packed_data; // radius + extrude.y + extrude.x

#define SHIFT_RIGHT18 1.0 / 262144.0

#define SHIFT_RIGHT20 1.0 / 1048576.0

#define SHIFT_LEFT18 262144.0

#define SHIFT_LEFT20 1048576.0

// unpack data(extrude(4-bit), radius(16-bit))

float compressed = a_packed_data;

// extrude(4-bit)

vec2 extrude;

extrude.x = floor(compressed * SHIFT_RIGHT20);

compressed -= extrude.x * SHIFT_LEFT20;

extrude.x = extrude.x - 1.;

extrude.y = floor(compressed * SHIFT_RIGHT18);

compressed -= extrude.y * SHIFT_LEFT18;

extrude.y = extrude.y - 1.;

// radius(16-bit)

float radius = compressed;

v_radius = radius;

看起来不错,我们将 vec4 + vec2 *2 + float 压缩成了 vec4 + float。当然了解了上述 float unpack 方法我们可以发现 vec4 中存储颜色的两个分量 float(rg) 和 float(ba) 其实也没有充分利用 24 位精度,各还有 8 bits 可以塞入其他数据。

现在我们需要度量优化后的效果,看看到底节省了多少 GPU 内存。

使用 MemoryInfra 度量 GPU 内存

关于 MemoryInfra 的用法我打算单独写一篇文章。从下面的界面中也能看出,要想看懂分析面板中以进程维度展示的内存数据以及详细分类(例如 blink_gc、cc、partition_alloc、gpumemorybuffer 等等),必须要了解一些 Chrome 的进程/线程模型、Blink 渲染引擎以及 GPU 内存在 Chrome 中的使用情况。这里推荐 @易旭昕 的一系列关于 Chrome Blink、cc、viz 的文章。下图来自「How cc Works」易旭昕:How cc Works 中文译文​zhuanlan.zhihu.com

这里我们只需要知道 MemoryInfra 是 Chrome 集成在 chrome://tracing 中的一个内存度量工具。Chrome 很多内置组件都会使用它,例如 V8 用它来度量 JS heap,而 GPU 组件则用它度量 OpenGL 和其他 GPU 对象的分配情况。MemoryInfra 界面

使用上述顶点数据压缩之后,在我的 DEMO 中可以看出 GPU 进程使用内存从 530M 下降到了 445M,而 DEMO 页面对应的 Renderer 进程从 22M 下降到了 14M。优化前 GPU 进程使用内存使用顶点数据压缩后,GPU 进程使用内存

来自 Cesium 和 Mapbox 的实践

Mapbox 广泛使用了对于颜色数据的压缩。而 Cesium 的这篇文章「Graphics Tech in Cesium - Vertex Compression」

「A Survey of Efficient Representations of Independent Unit Vectors」oct” 格式,只需要使用两个 8-bit 分量:

// https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Core/AttributeCompression.js#L43AttributeCompression.octEncodeInRange = function(vector, rangeMax, result) {

result.x = vector.x / (Math.abs(vector.x) + Math.abs(vector.y) + Math.abs(vector.z));

result.y = vector.y / (Math.abs(vector.x) + Math.abs(vector.y) + Math.abs(vector.z));

if (vector.z < 0) {

var x = result.x;

var y = result.y;

result.x = (1.0 - Math.abs(y)) * CesiumMath.signNotZero(x);

result.y = (1.0 - Math.abs(x)) * CesiumMath.signNotZero(y);

}

result.x = CesiumMath.toSNorm(result.x, rangeMax);

result.y = CesiumMath.toSNorm(result.y, rangeMax);

return result;

};

Cesium 就使用了这种方法来压缩法向量,随后将这两个 8-bit 按照上文介绍的方法又压缩到了一个 float 中。在 shader 中按照 float -> vec2 -> vec3 的顺序进行 unpack,得到原始的 vec3 单位向量:

// 解压缩存储了两个 8-bit 的 float,得到 vec2

vec3 czm_octDecode(float encoded)

{

float temp = encoded / 256.0;

float x = floor(temp);

float y = (temp - x) * 256.0;

return czm_octDecode(vec2(x, y));

}

// 解压缩 vec2 到原始 vec3 单位向量

vec3 czm_octDecode(vec2 encoded)

{

return czm_octDecode(encoded, 255.0);

}

vec3 czm_octDecode(vec2 encoded, float range)

{

if (encoded.x == 0.0 && encoded.y == 0.0) {

return vec3(0.0, 0.0, 0.0);

}

encoded = encoded / range * 2.0 - 1.0;

vec3 v = vec3(encoded.x, encoded.y, 1.0 - abs(encoded.x) - abs(encoded.y));

if (v.z < 0.0) {

v.xy = (1.0 - abs(v.yx)) * czm_signNotZero(v.xy);

}

return normalize(v);

}

对于只需要 12 bits 的纹理坐标,同样可以采用类似之前颜色数据的压缩方法:

// 压缩纹理坐标到 float 中AttributeCompression.compressTextureCoordinates = function(textureCoordinates) {

// Move x and y to the range 0-4095; var x = (textureCoordinates.x * 4095.0) | 0;

var y = (textureCoordinates.y * 4095.0) | 0;

return 4096.0 * x + y;

};

在文章BillboardCollection 中的压缩效果:The BillboardCollection and LabelCollection have a total of 18 attributes per vertex, each with various types and number of components. After packing and compression, the number is down to eight four-component floating-point attributes per vertex. For more details, see the BillboardCollection or its vertex shader.

最后我在查资料的过程中,在 Reddit 上看到了一个讨论

总结

对于顶点数据的压缩在地理信息展示场景中是很重要的,可以看出 Cesium 在这方面基本做到了极致。

在下篇文章中我会介绍 Chrome MemoryInfra 的使用方法,以及看懂内存分析数据所需要的一些知识。

参考

cesium 获取圆形边界位置_WebGL 中的顶点数据压缩相关推荐

  1. cesium 获取圆形边界位置_Cesium中级教程4 – 空间数据可视化(二)

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ Viewer中的Entity功能 让我们看看Viewer为操作e ...

  2. OpenCV之imgproc 模块. 图像处理(5)在图像中寻找轮廓 计算物体的凸包 创建包围轮廓的矩形和圆形边界框 为轮廓创建可倾斜的边界框和椭圆 轮廓矩 多边形测试

    在图像中寻找轮廓 目标 在这个教程中你将学到如何: 使用OpenCV函数 findContours 使用OpenCV函数 drawContours 原理 例程 教程的代码在下面给出. 你也可以从 这里 ...

  3. jquery获取元素在文档中的位置信息以及滚动条位置(转)

    jquery获取元素在文档中的位置信息以及滚动条位置 http://blog.csdn.net/qq_34095777/article/details/78750886     原文链接 原创 201 ...

  4. vue 点击div 获取位置_Vue中组件之间8种通信方式,值得收藏

    之前写了一篇关于vue面试总结的文章, 有不少网友提出组件之间通信方式还有很多, 这篇文章便是专门总结组件之间通信的 vue是数据驱动视图更新的框架, 所以对于vue来说组件间的数据通信非常重要,那么 ...

  5. IOS 正则表达式匹配文本中URL位置并获取URL所在位置(解决连接中文问题)

    IOS 正则表达式匹配文本中URL位置并获取URL所在位置(解决连接中文问题) 参考文章: (1)IOS 正则表达式匹配文本中URL位置并获取URL所在位置(解决连接中文问题) (2)https:// ...

  6. Java 针对每个人安装的位置不同的情况 从快捷方式中获取到安装位置 运行谷歌浏览器

    针对每个人安装的位置不同的情况 从快捷方式中获取到安装位置 运行谷歌浏览器 从桌面的谷歌浏览器快捷方式来获取对应的chrome.exe的地址,然后启动谷歌浏览器展示某个链接(也可以展示本地文件,但是需 ...

  7. 什么?RecyclerView中获取点击位置的接口被废弃了?

    本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每个工作日都有文章更新. 各位小伙伴们,大家早上好.上个礼拜,我在公众号的某篇文章下面看到这样一条留言: 什么?hol ...

  8. Android中获取用户附近位置

    最近项目中要获取用户附近位置,供用户选择,该功能分两步实现,第一,获取用户经纬度,第二利,用经纬度通过现有的地理位置接口获取附近位置,实现截图如下: 一.获取用户的经纬度 获取用户的经纬度,之前已经介 ...

  9. 在3D世界中的获取鼠标的位置

    原理 电脑的鼠标是在屏幕的2D坐标上运动的,而我们要获取的是3D世界中的一个三维坐标,在游戏引擎中的实现原理如下: 先获取鼠标在屏幕上的2D坐标. 结合摄像机平面计算出这个点在3D世界中的坐标. 从这 ...

最新文章

  1. AI一分钟|阿里AI鉴黄师或将取代人类;特斯拉私有化空头潜在利润超10亿美元
  2. mysql导出数据到s3_mysql导出数据库几种方法
  3. 推荐 2 个用 VS Code 直接浏览 GitHub 代码!只需要 1s !
  4. HTML自动获取地址,网页中自动获取经纬度值并在地图中显示当前位置实例代码...
  5. LeetCode 277. 搜寻名人(思维题)
  6. 基于C4.5神经网络集成
  7. SQLSERVER2005行版本控制的使用总结
  8. [OI学习笔记]最小生成树之Prim算法
  9. 2017-2018-1 20155321 20155330 《信息安全系统设计基础》实验四——外设驱动程序设计...
  10. 【开小灶】如何网盘批量转存?
  11. QT 资源管理器和.qrc文件的使用
  12. C++多线程函数_beginthread/_beginthreadex/CreateThread
  13. 03 在CentOS7中安装oracle11g
  14. python进阶高级技能:Python退火算法在高次方程的应用
  15. E.03.08. Scrapped Plans for London Concert Hall Sour Mood for U.K. Musicians
  16. php target当前页面,href标签target=_blank属性的妙用
  17. Java坑人面试题系列: 比对while与for循环(中级难度)
  18. 雷达感应模组技术,存在感应雷达传感器,智能电视开关机应用
  19. linux写日志文件
  20. “美亚杯”第三届中国电子数据取证大赛答案解析(个人赛)

热门文章

  1. 实验三 FFT及其在卷积计算和谱分析中的应用
  2. 华清远见-重庆中心-数据库阶段技术总结
  3. 计算机方面的英语杂志,哪种计算机英文杂志见刊快?
  4. 输电线路智能综合驱鸟器
  5. [转] 周志华:关于机器学习的一点思考
  6. mysql 数据库使用分享(多图解析)
  7. 五行JavaScript代码完成HaaS600矩阵键盘应用开发
  8. 年度盛宴系列——2012年最经典的10款 HTML5 游戏
  9. Windows10 把两张图片合并成一张图片
  10. PRNC(普乐币):全球通证资产及通证经济的发展趋势