antvis / g-webgl-compute

A GPGPU implementation based on WebGL.
MIT License
144 stars 15 forks source link

支持更好的直线绘制 #31

Open xiaoiver opened 3 years ago

xiaoiver commented 3 years ago

问题背景

之前在 L7 中的调研及实现方案:https://zhuanlan.zhihu.com/p/59541559

使用 Canvas 2D 或者 SVG 绘制直线时,通常会依据接头 lineJoin 和端点形状 lineCap 生成线的 path,然后使用颜色来填充这个 path。除此之外,还支持配置虚线等样式。

使用 WebGL/WebGPU 这样的更底层 API 绘制时,虽然在大量线段绘制时有明显优势(1000 条线段,使用 SVG),但在实现时却不能直接使用例如 gl.LINES 这样的原生方法。

gl.LINES 存在的问题

在一些场景下,尤其是涉及到地理信息的展示,直接使用原生的 gl.LINES 进行绘制存在一些问题:

因此我们得考虑将线段转换成其他几何图形进行绘制。

实现方案

常用的做法是沿线段法线方向进行拉伸后三角化。例如下图中线段两个端点分别沿红色虚线法向向两侧拉伸,形成 4 个顶点,三角化成 2 个三角形。 image

这里涉及到接头、端点形状、反走样等处理,问题是我们应该在 CPU 还是 GPU 中做?

在 CPU 侧进行预处理

之前在 L7 中选择在 CPU 侧做,所以你会看到考虑接头形状时增加顶点的处理。 这样的好处是,Shader 的写法简单,同时 GPU 内存占用较少(对比下面介绍的 GPU 处理方法)。

而局限性在于,由于顶点的拉伸是在 CPU 中完成,在透视投影下无法保持固定线宽。而这个特性在图场景中却是常见的。

image

GPU 处理

为了保持一致线宽,投影到屏幕空间后再进行拉伸即可,这需要在 Shader 中完成。由于 WebGL 不支持 Geometry Shader,因此除了当前顶点位置,还需要将前后顶点的位置一并传入顶点数据。这就引出了该方法的一个缺点:GPU 内存占用较多。

基于 Three.js 的实现就是用了这种方法。但并不支持圆角接头。

而 GeoJS 的实现是我认为目前最好的。