在 WebGL 中渲染 Polyline(二)

CesiumJS 在渲染 Polyline 这事儿上下了不少功夫。纵观 WebGL 发展的这十几年,对 Polyline 的渲染已经有很多资料,然而 CesiumJS 却停留在当年的效果,对斜接接头过于锐角的情况、抗锯齿的优化都没有改进,这点是比较遗憾的。

在 WebGL 中渲染 Polyline(二)
3D模型在线预览提供多种低代码平台3D模型在线预览解决方案,实现了将多种3D模型格式无缝集成到低代码业务表单中。这意味着用户可以在不离开低代码平台的情况下,直接查看和操作3D模型,极大地提升了数据可视化的效果和用户交互体验。

顶点着色逻辑细节

在屏幕坐标系下偏移顶点

下图用中间黑色的折线来绘制出红色的“折线”,在屏幕坐标系下,黑色的折线被偏移了等宽的距离。

下图是拐点内侧(锐角)的细节,设绿色向量为 u,黑色向量为 v,显然,拐点要沿着未知的向量 u 进行偏移,而 u 就是要在顶点着色器中计算的。

根据当前顶点和前后顶点的坐标,轻易可以计算得到 v 的方向向量。知道 v 后,就可以算出 u,它就是这个拐角的角平分线方向。

现在只需要算出 u 的长度即可,根据三角函数不难得出:

对于任何向量 p 和 q,有:

如果我们把 û 和 v̂ (译者注:这种字母一般就是指单位向量)当作 xOy 平面上的三维向量,那么把它俩代入即可。

由于 û 和 v̂ 是单位向量,且在 xOy 平面上,那么 z 分量就是 0,所以可简化为:

GLSL 代码如下:

sinAngle 非常小(逼近 0),容易出现尖锐角问题。还要确保拐点处的两个顶点能按正确的方向平移(此例子中即 u 或 -u 方向)。

裁剪至近平面

同上文解释的一样,要把线段在顶点着色器中提前裁剪到近平面,而不是等 WebGL 自己做。下面这个函数就实现了这个思路:

函数中的注释描述了发生裁剪(或剔除)的条件,这个函数以近平面的法线为 vec3(0.0, 0.0, -1.0)、且距离原点的距离就是 czm_currentFrustum.x 进行了简化计算。

编码/解码单位向量

当被限制到 8 个 VertexAttribute 时,Cesium 团队使用球形贴图变换(Spheremap Transform,在地图学里又叫做兰伯特方位等积投影)来对当前顶点的上一个、下一个顶点的单位向量进行编码。这样,两个 vec3 向量就能编码到一个 vec4.

下面是把两个单位向量压缩到两个分量的 JS 代码:

在 GLSL 中对应的解压缩代码(用于顶点着色器):

总结

CesiumJS 在渲染 Polyline 这事儿上下了不少功夫。在本科 GIS 中,把线生成面,而且是按宽度生成,是一个非常入门的工具:缓冲区分析。

但是,在如履薄冰的图形渲染管线中,要把单纯表示线的顶点们通过平移的手段“变”成面,而且还要保持接头的样式(此例用的是斜接接头),还要考虑 WebGL 的裁剪问题、WebGL 顶点属性不够用的问题,就变得比较复杂了。好在图形学大佬们早在数十年前就搞定了这些问题。

综上,可以得出 CesiumJS 渲染空间折线的技术难点和解决方法:

  • 使用绘制面的方法替代原生 WebGL 线不能渲染线宽的问题
  • 使用端点复制成两个,并沿着特定方向、指定宽度平移的办法,实现渲染面
  • 为了保证折线在屏幕上始终宽度如一,需要在顶点着色器中先一步到视口坐标系下进行偏移,再反算到裁剪坐标,以衔接 WebGL 渲染管线
  • 为解决有端点被近平面裁剪的问题,就需要把端点平移到近裁剪面上
  • 在端点处,如果相邻的前后点都存在,那么它是一个中间点,就要处理接头问题,CesiumJS 目前实现的是斜接(即「miter」型)类型
  • 因为 WebGL 没有几何着色器,所以复制顶点的工作在 JS 这边让 CPU 完成计算

纵观 WebGL 发展的这十几年,对 Polyline 的渲染已经有很多资料,然而 CesiumJS 却停留在当年的效果,对斜接接头过于锐角的情况、抗锯齿的优化都没有改进,这点是比较遗憾的,希望只是 CesiumJS 在憋大招吧。

在 WebGL 中渲染 Polyline - 索引

在 WebGL 中渲染 Polyline(一)

在 WebGL 中渲染 Polyline(二)

NSDT场景编辑器 | NSDT 数字孪生 | GLTF在线编辑器 | 3D模型在线转换 | UnrealSynth虚幻合成数据生成器 | 3D模型自动纹理化工具
2023 power by nsdt©鄂ICP备2023000829号