DIY 多面体镜面万花筒

前几天在网上看到了一位艺术家 Anthony James 的展览作品,使用多面体和镜子生成万花筒的效果,觉得很有意思:

这个作品的大致原理是:

  1. 多面体的外壳是透明的玻璃,内表面铺有反光膜。
  2. 多面体的每条边朝向内部的一侧安装了 LED 光源。
  3. 光源的光线在内部反复反射,每次反射时大部分光线会被反光膜反射回内部,一部分光线穿过外壳被外部观察到,就看到了层层叠叠的万花筒图案。

YouTube 上有个博主手把手地展示了怎样从头 DIY 一个这样的模型:视频地址。我表示这顿操作虽然看着不算难,但对我这种懒人来说还是有点过于折腾了。

不过没关系,咱可以编程实现呀,不就是把多面体的数据算出来,然后光线追踪渲染么。折腾了一周左右搞出来了:代码地址,做出来的效果和真实视频中的样子也差不了多少:

上面的动画演示的是菱形十二面体 (rhombic dodecahedron),它有个独特之处是自身在镜子反射下成的像可以密铺整个空间 (space filling)。

石榴籽的形状其实近似于菱形十二面体,这使得它们可以紧紧的挨在一起构成密铺的结构:

这种可以密铺整个空间的多面体非常少,正方体当然是其中之一,但是其它的正多面体 (正四面体、正八面体、正十二面体、正二十面体) 都不行。

截顶的正方体看起来也是可以的:

但这个例子是有点特殊的:观察者从外面看它确实密铺了整个空间,但是如果观察者身处多面体内部的话,看到的就不再是一个完整的的密铺结构。想象一下站在多面体内部的一个三角面前面往里看,这时除去这个三角形之外八面体的其它边都是看不到的,否则它们关于这个三角面的原像都在多面体内部,这不可能。站在外部的观察者能看到整个密铺是因为他可以同时看到所有镜子后面的情况。

下图显示的是截顶的八面体,你觉得它有什么问题不?

David Eppstein 评论说 可以把截顶八面体对半切开,两部分转一个角度再拼上,然后得到的就是一个内部镜像可以密铺整个空间的多面体。不过我的几何直觉差的太远了。

这个代码可以渲染绝大部分的 uniform polyhedron 及其对偶,入口函数为

1
2
3
4
5
6
7
generate_polytope_data(
coxeter_diagram,
trunc_type,
extra_relations=(),
snub=False,
dual=False
):

各参数含义如下:

  • coxeter_diagram 是多面体对称群对应的 Coxeter-Dynkin 图。
  • trunc_type 设置多面体的截断类型。
  • extra_relations 用于指定星状多面体所需的额外生成关系 (目前的渲染代码不太支持这种多面体)。
  • snub 选择是否是手性多面体。
  • dual 选择是否渲染对偶多面体。

关于这些参数的具体解释,以及怎样通过它们计算多面体的数据,请参考我之前的 一篇文章

代码中使用的多面体数据其实就是把每个面的三维坐标根据顶点个数分别放在不同的 vec3[] 数组里面,你也可以将任何其它多面体的数据代入来观察效果,比如多面体 Gyrobifastigium 1 给出的效果如下所示:


  1. 感谢陈浩老师告知,我才知道有这么个名字诡异的多面体。↩︎

 | 

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器