使用 Hash 函数生成过程纹理
使用 Hash 函数生成过程纹理
我这种人很容易被简单规则创造复杂行为所吸引。 前几天,我需要一个简单的 Hash 函数,将 (x,y) 坐标映射到颜色,然后发现了一个非常简单的方程,结果却出奇地丰富。 因此写这篇文章,来讨论和把玩这个函数。
简而言之: 布尔谓词 (cxx+cyy+cxyxy+cx2x2+cy2y2) mod m < τm, 非常丰富且美观。
例如,在其他参数固定的情况下,改变 cxy:
在本文的剩余部分,我们将尝试剖析这个函数。 如果你更喜欢自己玩,请查看 hash playground。
想法
我当时尝试做一个遵守 2-bit 调色板的游戏。 严格按照规则,这意味着没有插值或抗锯齿 —— 屏幕上的像素应该只有 4 种颜色之一。 所以我需要能够完美对齐到屏幕的纹理。 并非完全新颖的解决方案是:
Hash (x, y) 屏幕空间像素坐标,并用它来选择颜色。
事实证明,只要相机和视口是固定的,这个方法就没问题,而且这个非常简单的 Hash 函数非常多变且有趣:
(cxx+cyy+cxyxy+cx2x2+cy2y2) mod m < τm
例如,在 Python 中:
c_x, c_y, c_xy, c_xx, c_yy = 1, 1, 0, 1, 1
m, t, w, h = 64, 0.5, 128, 128
x = np.arange(w)[:, None]
y = np.arange(h)[None, :]
h = (c_x*x + c_y*y + c_xy*x*y + c_xx*x**2 + c_yy*y**2) % m < t*m
display(PIL.Image.fromarray(h))
一点理解
让我们尝试建立一些数学知识,以理解这些模式背后的一些结构。 我们将把 () 之间的表达式称为 body。 请注意,本节中的所有内容都使用 128 的画布宽度、除数 m=64 和阈值 τ=0.5。
如果我们的 body 只是 (x) 呢? Hash 不依赖于 y,并且对于 x < 32 应该为 true,对于 32 ≤ x < 64 应该为 false。 它应该在我们的 128 的画布宽度上重复两次:
(x2) 怎么样? 我期望再次看到一个重复两次的模式,但我错了 —— 我们得到了 4 个副本:
也就是说,该模式似乎在 m/2 而不是 m 之后重复。 让我们看看如果我们将 m/2 添加到 x 会发生什么:
(1)(x+m/2)2 mod m (2)=(x2+mx+m2/4) mod m (3)=x2 mod m (如果 m 是 4 的倍数)
由于 +m/2 消失了,因此该模式必须每 m/2 个像素而不是 m 个像素重复一次。
为了理解模式本身,它就像一种极端的混叠效应。 从左到右扫描,首先你跟随一个二次曲线:黑色然后蓝色。 然后你跳过 m 边界,你再次得到相同的东西,但更快。 甚至更快。 最终,你跳得如此之快,以至于每隔一个像素之后你都会跳到下一个 m 的倍数。 就像观看直升机叶片旋转的视频一样,模式开始向后运行。 频率再次减慢,直到下一个长黑块,那时你每像素跳过 ≈m。 然后模式重复。
进入 2D, (x2+y2). 为了获得真正有趣的模式,我们需要使用 x 和 y。 由于圆的方程是 x2+y2=const,因此圆是 iso-hash 线,这些线清晰可见。 由于表达式是使用 x2 和 y2 构建的,因此我们在每个轴上获得每个 m 2 个副本,因此总共有 (2⋅128/64)2=16 组圆。
增加频率, (2x2+2y2). 毫不奇怪,将 cx2 和 cy2 加倍会使模式的频率加倍,相当于将 m 从 64 减半到 32。
交叉项, (xy). 我最喜欢的一些模式来自调整 cxy。 即使是简单的 body xy 也很有趣。 我没有太多的解释,除了 iso-hash 是 xy=const,产生一系列倒数曲线 y ∝ 1/x,这似乎与形成令人愉悦的螺旋形的黑色和蓝色线条相匹配。
这就是我对理解的全部内容; 我相信可以从模运算的数学中说出更多,这在很大程度上超出了我的理解范围。
一些最爱
这是经过一些闲暇修改后的一些个人最爱。 请注意前两个方程的相似性,它们产生了非常不同的模式。
扫描阈值 τ 特别有趣,这对于像爆炸这样的效果非常有用。
总结
我希望你喜欢花几分钟盯着一个简单函数的复杂性! 如果你还没有这样做,请尝试 hash playground。 也许你可以为你的游戏生成一个新的纹理,或者打印一个手机壳(我的下一步)。
看看你是否可以在下面我的 game-jam 游戏剪辑中发现一些这些 Hash 纹理。 如果你认为自己是极其有限的目标受众(他们想学习一种类似汇编的语言只是为了玩一个简短的网页游戏)的一部分,也许可以看看它:C-crits。
Happy Hashing!
注意:此处表达的所有观点或意见均为作者撰写时的观点,不代表任何雇主或其他组织(过去或现在)的观点。 请通过 在 GitHub 上提出 issue 告诉我错误或遗漏的引用。