朝闻道

Shadertoy 作品:三维 Poincaré 空间中的双曲蜂巢

赵亮 / 2020-12-22


周末在家手痒,又写了一个 Shadertoy 项目,演示三维 Poincaré 单位球空间中的双曲蜂巢:(抱歉由于超采样导致帧率比较卡,主要是为了效果好看)

默认绘制的蜂巢是正则 (5, 3, 5),如果要查看其它不同的蜂巢需要修改代码中 diagramactive_mirrors 的值,这两个值不能是任意的,维基百科上列出了所有可能的组合,这里不再详细介绍。此外默认超采样指数 AA=3,即每个像素采样 9 次。(似乎 AA=4 的效果最好)

注意这个程序要求输入的 Coxeter diagram 的前三个顶点组成的子图必须是有限型 (spherical type),所以对 (4, 4, 4) 或者 (3, 6, 3) 这样的仿紧类型你不能直接按顺序输入 (4, 2, 2, 4, 2, 4) 或者 (3, 2, 2, 6, 2, 3),而是需要交换后两个镜面的次序,输入 (4, 2, 2, 2, 4, 4) 或者 (3, 2, 2, 2, 6, 3) 就可以了。

这个项目的数学原理并不多么复杂,本质上还是 Wythoff 构造法,只需要对 Coxeter 群和双曲几何中的 hyperboloid 模型有一定了解即可。但是要在 shader 里面用 raymarching 的方法把它画出来其实相当麻烦:

  1. 首先 raymarching 方法是在三维欧式空间中进行的,而我们要绘制的对象要在四维的双曲空间中计算好以后投影到三维,怎么保证在三维空间中 raymarching 的时候,它在四维空间的 hyperboloid 上对应的位置也能在双曲度量下一步步 "蹭" 到目标物体,这个要费一番脑筋。这个项目中采用的方法来自 knighty,这是一个 "保守" 的距离场估计方法,可以在每次 raymarching 的时候行进一个不太理想但是保证 "安全" 的距离,然后经过一定次数的迭代以后逐渐逼近场景中的物体。

  2. 双曲度量下同样大小的物体,当接近边界球面时其欧式度量下的大小是以指数级的速度衰减到 0 的,所以你很快就会遇到浮点数精度的问题,导致场景远处的细节出现 "毛刺感"。这里我们可以取个巧,加点雾化的效果将远处的细节隐藏起来。如果非要显示的话就把计算法向量时的差分步幅对远处的物体调得小一点。

以下是一些效果图:

我个人感觉对双曲蜂巢的渲染效率和渲染效果很难有同时理想的解决方案,特别是接近无穷远边界的细节不太好处理。加上我写 shader 水平也不太够,希望后面再加以改进吧。