警告:多图杀猫

背景

随着小朋友日夜长大,好奇心也不断增长。我作为家长自然希望能稍加引导,以免他长成个熊孩子。于是就去油管上找了下有啥面向初学者的编程课程,结果很有缘分地找到了一个叫The Coding Train[1]的频道。主播名叫Daniel Shiffman,订阅粉丝超百万。为什么说有缘分呢,因为Dan之前写过本书叫"The Nature of Code",国内也引进过,译作《代码本色》,并且在我的书架上积灰了多年:

代码本色

书中使用Processing模拟了一些自然系统。Processing基于Java,包含了自己的IDE。而在油管频道中Dan则是大量使用了p5.jsp5.js继承了大部分的Processing的API,并且可以直接在浏览器里运行,对于初学者来说会更友好。

频道的官网上有一块叫Coding Challenges[2],是由频道粉丝提出一些小的idea,然后由Dan在直播中尝试用p5.jsProcessing来实现,同时解释一些基础的编程知识。虽然中途时不时会翻车,但最终的实现效果通常都很不错。比如这个SuperShape3D[3]:

SuperShape3D

我家小朋友小时候就挺喜欢玩:

SuperShape3D

又比如这个Phyllotaxis[4]:

Phyllotaxis

可以看出Dan真的很喜欢彩虹色。

不过有些东西就比较抽象,可能我比较喜欢,但小朋友就没啥兴趣。比如这个Kaleidoscope Snowflake[5]:

Kaleidoscope Snowflake

为了找到能让小朋友满意的玩意儿,我利用业余时间把频道里的这一块视频都撸了一遍[6],前后断断续续花了将近一年的时间。要不是因为疫情隔离原因Dan停更了一段时间,以及现在Dan好像又在写The Nature of Code 2的样子,导致后期这部分视频数量急剧减少,不然可能会花上更多的时间。

遗憾的是,撸完了现有的视频也没找到更多能让小朋友满意的东西。不过有些东西让我觉得挺有意思的,就在这里记录下。

10PRINT

提问:生成下图需要多少代码?

10PRINT

答案是一行C64 Basic代码[7]:

10 PRINT CHR$(205.5+RND(1)); : GOTO 10

当然用p5.js实现的话不只一行[8]。信息时代早期人们的创造力着实让我着迷。

Perlin噪声

似乎IT行业中,除了游戏行业就很少会用到Perlin噪声。而在游戏行业里,最常用到Perlin噪声的地方就是程序化生成地形[9]:

Terrain

大名鼎鼎的Minecraft在生成地形时就大量使用了Perlin噪声[10]。此外Dan还展示了如何用多维的Perlin噪声实现那些看似随机,实则无限循环的动图[11]。

给图标磨个圆角

据说某米今年花了200万给自家的图标磨了个圆角[12],甚至还给了个公式,让人不明觉厉。然而早在5年前的2016年[13],Dan就已经手撸过同一个公式了[14]:

SuperEllipse

Dan作为纽约大学艺术学院的副教授,不知道在设计界有多大的名气?“东瀛罗永浩”的设计团队会是在Dan这里得到的灵感吗?

傅立叶变换

这是我个人比较喜欢的一个主题。首先,一系列大小不同的相切圆环的运动轨迹居然可以形成某种分形[15]:

FractalSpirograph

然后如果都在同一个方向上叠加的话就可以重现傅立叶变换[16]:

Fourier Series

依稀记得当年学数字信号处理的时候,老师让我们自己手绘不同周期的正弦波并叠加,以此来直观感受对方波的傅立叶变换。而现在的小朋友就可以很方便地在浏览器里手撸一个出来。我看着他们,满怀羡慕。

上面是一维的情况,简单扩展到二维的话可以画出一些美妙的图形[17]:

Lissajous

而对任意的平面点坐标序列做傅立叶变换的话,就可以绘制任意图形[18]:

Fourier Transform Drawing

3D

p5.js不但可以绘制2D图形,绘制简单的3D图形也很方便[19]:

Bees and Bombs

不过毕竟只是个lib,连个场景图都没有,做略微复杂点的场景就很痛苦[20]:

Rubik's Cube

Dan在做这个魔方的时候也是屡屡翻车。

Pi Collisions

这个我个人也非常喜欢。谁能想到两个滑块之间的完全弹性碰撞次数居然跟π有联系[21]:

Pi Collisions

著名的3b1b对此也有解释[22]。另一篇解释[23]中还牵涉到了量子计算,其中提到了把大滑块看成了相同质量的小滑块的组合,而这正是Dan在实现时用到的方法!

Ray casting

最近某来步了特斯拉自动驾驶“杀人”的后尘引起热议[24]。跟特斯拉一样,某来也没有激光雷达。Musk很早就说过傻瓜才用激光雷达[25]。那么如果有完美的激光雷达会怎样?借助Dan的小项目告诉你[26]:

Ray Casting

如果在发出的每条射线的方向上都可以精确得知自己到障碍物的距离,那么就可以渲染出一个伪3D的场景[27]了:

Rendering Ray Casting

早期的FPS游戏如Wolf3D和Doom等都用了这样的方法。相比纯视觉的方案,实现简单可靠,还有完美的可解释性,确实是连傻瓜都会用的方法。

MetaBalls

MetaBalls

这本来是个平平无奇的小项目[28],然而我的实现却跟Dan的实现在运行效率上差了不只一点半点。仔细比对以后发现差异就在一个destructure操作上:

const [dx, dy] = [x - bx, y - by];

这个操作是在双重循环里的,改成单独赋值的话效率明显提升:

const dx = x - bx
const dy = y - by

尝试做了一下benchmark,发现在Firefox里destructure操作有明显的性能问题[29]:

benchmark

Chrome里结果更接近一些,但destructure操作的性能还是不如直接赋值。所以说即便浏览器普遍都原生支持了ES6语法,使用一个ES5的transpiler还是很必要的。

先就这样吧,如果Dan以后又更新了哪些有意思的东西我再来更新一波。

参考资料