Loading...
墨滴

张春成

2021/09/01  阅读:35  主题:默认主题

WebGL绘图(之二)

WebGL绘图(之二)

不得不说WebGL是个坑,坑在哪呢? 一是计算机语言,二是高等数学。


复数空间

我使用WebGL绘制了一个可交互的, 形如

的复数空间。

【这是一段棒到不行的视频】

复数就是一个五彩斑斓的世界, 我使用HSV颜色空间进行绘制,其中

  • 色相H维度,代表复数 值的相角;
  • 饱和度S和色度V,代表复数 值的模数,模越大颜色越偏白,模越小颜色越偏黑。

门槛

可视化前端是D3P5ThreeJS工具的天下, 完全可以胜任制图的需求。

WebGL可以进一步提高这些玩意的性能。 但任何高性能的东西都有门槛, 我觉得WebGL的门槛主要在两点,

  1. 它需要多种语言的联合编译, 以REGL为例,它就是C++JS的合体

    JointCoding
    JointCoding

    这玩意在前面交互环境下的Debug过程, 需要的不仅是代码知识, 更多的是需要大量的“想象”力。 以及C++固有的各种语法大坑。

  2. 我们把“想象力”部分稍微展开说一下, 就是需要使用一些基本的高等数学知识。 下面我举一个例子,说明为基础数学不足以支撑这样的绘图方式。

等值线框

注意到图中有一些线框,就是黑色的较细的一条一条的线

Curve1
Curve1

这些线框,可以理解成复数空间中的等值线。 该复数空间的构造方式为

另一个空间如下图所示

Curve2
Curve2

该空间的构造方式为

这个图中的线框可能比较好理解。 它们十分地像初高中学到的二次曲线, 事实上,它们在实数空间中的映射就是满足四次方程

的平面曲线。

在任意一条连续的黑色曲线上,我们可以说(其实也就是)

在任意一条连续的黑色曲线上,都满足方程

其中, 代表某个固定值。

而如何绘制这样的线框,就已经涉及了高等数学的守门员

高等数学的守门员:全微分的概念

线框的绘制

初等解法

在初等数学的思想下,如何解决线框的问题呢?

很简单, 在每个像素附近都求它与特定目标值,如 ,之间的距离。 若距离小于特定值(用 表示), 则代表该像素属于某条线框, 再将它的颜色涂黑就可以了。

但在实际操作上无法实现, 即使实现了, 其效果也很难令人接受。

比如,在第二张图中中间的黑色区域就是这样绘制的效果。 为什么会发生这样的情况? 因为在 的函数形式下, 靠近零点的像素与远离零点的像素的 值的尺度完全不同, 在固定 值的情况下, 势必会造成靠近零点的线框过宽,而远离零点的线框过窄, 其效果极差。

全微分解法

这时就需要高等数学介入,从全微分的角度解决这个问题

RectGrids
RectGrids

可以精准地绘制这些线框。

原理也并不复杂,

sineWave
sineWave

我们拿上图为例, 这就是一列普通的正弦波, 绘制线框的问题,可以等价于找到它的各个极大值或极小值点, 进一步的,它可以等价于找到它的全部一阶导数零点。 对于一元简单连续函数, 一阶导数完全可以解决问题。

而由于我们要处理的空间为复数空间, 因为需要考虑二元连续函数。 函数形式并不复杂, 只需要将上述导数变成求解局部全微分即可。 类似下面这个东东

TangentSurface
TangentSurface

这就跳出了初等数学的局限。 虽然在一定程度上,增加了计算的复杂性, 却提高了线框的准确性。

代码实现

在代码实现部分,需要额外使用到WebGL的微分扩展包, (GL_OES_standard_derivatives[1]) 以及其功能强大的fwidth函数。

// Extension
#extension GL_OES_standard_derivatives : enable

// Core
vec2 fz;
vec2 gridSpace;
fwidth(mod(fz, gridSpace));

// Others

具体代码可见我的ObservableHQ[2]工程。

参考资料

[1]

GL_OES_standard_derivatives: https://developer.mozilla.org/en-US/docs/Web/API/OES_standard_derivatives

[2]

ObservableHQ: https://observablehq.com/@listenzcc/complex-space

张春成

2021/09/01  阅读:35  主题:默认主题

作者介绍

张春成