乐趣区

关于javascript:译Filed-Play简介

引子

在尝试数学函数可视化的时候,发现了一个乏味的库 Field Play,对 README 中的阐明进行局部翻译记录,做个初步理解。

  • Origin
  • My GitHub

What?

让咱们为网格上的每个点指定一个向量 (1, 0)。这意味着咱们有一个箭头,指向左边:

假如这些向量代表速度。如果咱们把一千个粒子扔到这个网格上呢?它们会怎么口头?

当咱们给空白区上的每个点调配一个向量时,咱们创立了一个称为 向量场(Vector Field) 的数学构造。

让咱们创立一个更乏味的向量场:

  • y 坐标为偶数的点失去向量 (1, 0)
  • y 坐标为奇数的点失去一个相同的向量 (-1, 0)

咱们再次投下几千个粒子,看看会产生什么:

上述能够用一个公式示意:

v.x = -2.0 * mod(floor(y), 2.0) + 1.0;
v.y = 0.0;

整数相除 y/2 后的余数可能是 1 或 0。而后咱们变换余数,使最终向量为 (-1, 0)(1, 0)

到目前为止,咱们只应用了速度向量的一个重量 v.x,粒子只程度挪动。让咱们试着设置所有两个重量,看看会产生什么

v.x = -2.0 * mod(floor(y), 2.0) + 1.0;
v.y = -2.0 * mod(floor(x), 2.0) + 1.0;

哇!两个简略的操作,最终的动画看起来像一件艺术品!

事实证明,向量场是非常灵活的生成框架。

How this project works?

这个我的项目的灵感来自 Vladimir Agafonkin 的文章:How I built a wind map with WebGL。Vladimir 演示了如何齐全在 GPU 上以每秒 60 帧的速度渲染多达 100 万个粒子。

我应用了简直雷同的技术,但做了一些批改:

  1. 向量场是用着色器语言 GLSL 代码定义的,因而数学公式能够自在示意。
  2. 粒子的地位在 GPU 上用四阶 Runge-Kutta 法计算。
  3. 每个维度 X 和 Y 都是独立计算的,因而咱们能够更精确地存储地位。
  4. 应用 panzoom 库增加了平移 / 缩放性能。
  5. 向量场定义应用 query-state 库保留在 URL 中。这样你能够不便的把你的向量场退出书签 / 分享。

Float packing

基于 WebGL 计算的核心思想非常简单。

GPU 能够十分疾速地渲染图像。每个图像都是像素的汇合。每个像素只是一个代表色彩的数字,通常以 32 位(RGBA 格局)写入。

但谁说每像素的 32 位必须代表一种色彩?为什么咱们不能计算一些数字,并将其存储到 32 位?这个数字能够是,例如,沿着某个速度向量的粒子的地位 …

如果咱们这样做,GPU 依然会将这些数字视为色彩:

侥幸的是,咱们不用让用户看到这些看似随机的图像。WebGL 容许在称为 帧缓冲区 (frame buffers) 的“虚构”屏幕上渲染内容。

这些虚构屏幕只是视频存储器中的图像(纹理)。有了两种纹理,咱们能够利用 GPU 来解决数学问题。在每一帧上,算法的工作原理如下:

  1. 通知 GPU 从“background”纹理读取数据;
  2. 通知 GPU 应用帧缓冲区将数据写入“screen”纹理;
  3. 用“screen”替换“background”;

从实践上讲,这应该能很好的运行。实际上存在一个问题。WebGL 不容许将浮点数写入纹理。所以咱们须要将一个浮点数转换成 RGBA 格局,每个通道 8 位。

在 Vladimir 的文章中,应用了以下编码 / 解码模式:

// decode particle position (x, y) from pixel RGBA color
vec2 pos = vec2(
    color.r / 255.0 + color.b,
    color.g / 255.0 + color.a);
... // move the position
// encode the position back into RGBA
gl_FragColor = vec4(fract(pos * 255.0),
    floor(pos * 255.0) / 255.0);

在这里,粒子的 XY 坐标都存储在一个 32 位的数字中。我一开始就应用了这种办法,它在桌面和 Android 手机上运行良好。

然而,当我在 iPhone 上关上一个网站时,令人不快的惊喜正等着我。没有任何显著的起因就呈现了重大的瑕疵。

比拟同样的代码在桌面(左)和 iPhone(右)上运行

更蹩脚的是,当向量场是动态的(所有中央速度为 0)时,iPhone 上的粒子始终在挪动:

我查看了申请的浮点分辨率是否设置为最高可用(highp)。然而,这些瑕疵还是不言而喻。

How can we fix this?

我不想应用启用浮点纹理这种最简略的解决办法。它们没有像我心愿的那样失去广泛支持。相同,我做了多年来非 GPU 编程通知我不要做的事件。

我决定解决数千个常微分方程,而不是每帧一次。但每个维度都有一次。我将向着色器传递一个属性,通知它须要将哪个维度写入此“draw”调用的输入:

if (u_out_coordinate == 0) gl_FragColor = encodeFloatRGBA(pos.x);
else if (u_out_coordinate == 1) gl_FragColor = encodeFloatRGBA(pos.y);

在伪代码中,它如下所示:

Frame 1:
  Step 1: 嘿,WebGL,将 u_out_coordinate 设为 0,并将所有内容渲染进 `texture_x`;Step 2: 嘿,WebGL,把 u_out_coordinate 设为 1,而后把所有内容再次渲染进 `texture_y`;

咱们解决了同样的问题,除了解决方案中的 x 重量,咱们扔掉了所有货色。而后对 y 反复一遍。

这对我来说仿佛很疯狂,因为我认为这会影响性能。但应用这种办法,就连我的低端安卓手机也没有遇到问题。

encodeFloatRGBA() 应用所有 32 位将浮点编码为 RGBA 向量。我在 stackoverflow 的某个中央发现了它的利用,我不确定这是否是最好的解决形式(如果你晓得得更好,请让我晓得)。

好消息是瑕疵隐没了:

参考资料

  • fieldplay github
退出移动版