Cesium中实现三维风场粒子效果
三维风场
三维风场通常指的是在三维空间中,记录每一点的风速和风向分布情况的数据。它不再像地面天气预报那样只描述一个二维平面(例如地面以上10米)的风,而是完整地描述了风在水平方向(东-西,南-北) 和垂直方向(上-下) 上的运动。
简单来说,这个数据是一个三维矢量场,空间中的每一个点都有一个风速和风向确定的矢量箭头。
通常,三维风场数据包含三个重要的分量:
-
U 分量:表示东西方向的风速。
- 正值:西风(风从西边吹来,向东吹)。
- 负值:东风(风从东边吹来,向西吹)。
-
V 分量:表示南北方向的风速。
- 正值:南风(风从南边吹来,向北吹)。
- 负值:北风(风从北边吹来,向南吹)。
-
W 分量:表示垂直方向的风速。
- 正值:上升气流。
- 负值:下沉气流。
我们日常感知的风主要是U和V分量(水平风),而W分量(垂直风)通常非常微弱(除了一些特殊天气系统外),但它在天气系统的形成和发展中起着至关重要的作用。
三维风场粒子
用粒子展示风场是三维风场其中一种展示手段。
粒子的计算方式从本质上分为两种,一种CPU计算,一种GPU计算。
CPU计算实现起来比较简单,但是无法承载大量的粒子同时计算,加上js是单线程语言,容易阻塞主线程的其他计算。
GPU计算稍微复杂一点,但是得益于GPU并行计算的优势,GPU计算能支持海量的粒子同时更新位置,很适合来做三维风场粒子这种简单而重复的计算。
本文实现的粒子效果,就是通过GPU的方式来进行计算的。
GPU粒子实现思路
粒子计算
利用WebGL来使用GPU计算三维粒子的位置思路比较简单:
- 我们首先需要在着色器中弄两个随机数算子:
- 用于生成粒子位置的随机数算子,生成一个 vec3(0,0,0) 到 vec3(1,1,1) 的三维随机数。
- 用于生成粒子的生命值的随机数算子,这个随机数算子的生成范围我设定在 [0.3, 1] 之间。
- 利用上面的随机数算子生成一张记录初始粒子状态的纹理,这张纹理的每个像素记录了一个粒子的位置和当前生命值(RGB三个通道存储粒子位置,A通道存储生命值)。
- 根据三维风场数据生成一张三维纹理,存储UVW信息。
- 根据粒子的位置采样UVW纹理,获取当前位置风的“方向”和“风速”,根据这些信息计算当前粒子在下一帧的位置,并且每次更新都减少粒子的生命值。如果生命值小于或者等于0,那么重新根据随机数算子,计算粒子重生的位置和新生命值。
实际的计算流程图如下:
粒子展示
到这里我们已经完成了三维粒子的位置计算,但是怎么展示呢?
正常的思路我们可能会想到,既然粒子的位置信息都记录在了纹理上,那么我们读取纹理,然后利用 PointPrimitiveCollection 绘制出来不就行了吗。
实际上这种做法效率并不高,因为要涉及将GPU里面的纹理信息提取到CPU,再通过CPU组织粒子坐标信息发送到GPU进行渲染。会存在GPU和CPU之间信息传递的消耗。
更高级的做法是直接在GPU当中对纹理的每个像素进行解析和渲染,那么怎么才能做到渲染一堆点,但是每个点分别采样不同的粒子纹理像素呢?
我们可以使用实例化渲染的技术来实现这一步骤,关于Cesium的实例化渲染,大家可以看这篇文章
Cesium 高性能扩展之 DrawCommand(四):阴影和实例化
具体做法是在VertexArrayAttribute中我们传每个点的索引值(用来获取当前点应该采样粒子纹理上的第几个像素),然后在顶点着色器中将这个索引值换算成uv坐标,来采样粒子纹理,并计算粒子的实际位置,以此来作为当前点的位置。