【翻译】用 Blender 和 动画节点 生成分形


theme: orange
highlight: zenburn

本文正在参加「金石计划 . 瓜分6万现金大奖」

英文链接

Generating Fractals with Blender and Animation-Nodes | Alex Martinelli

译文

前言

What: 这是一个使用 Blender 生成分形视觉效果的 教程+例子+资源 的集合。我将介绍 递归n-flakesL-SystemMandelbrot/Julia 集和其派生 等主题。

Why: 如果你着迷于分形图案,对 Blender 的程序生成感兴趣,并想更好地理解和亲身体验动画节点和着色器节点的话。

Who: 这里的所有内容都是基于 Blender 2.8.2 和动画节点 v2.1 。同时也依赖于一些简短的代码片段(Python≥3.6)。

分形

“beautifully intricate, yet so simple” ——
“精美复杂,却又如此简单”

分形是具有分形维度(fractal dimensions)的形状。这源于我们衡量它们尺度的方式。理论上的分形在不同尺度上是无限自相似的。

对于我们的设置,我们不关心纯粹的理论上的分形,因为它们在 Blender 中是 无法实现 的,除非在非常特定的情况下。我们最关心的是精细的结构(不同尺度的细节)和自相似的自然外观(由明显的更小的自身副本组成)。

更多关于分形的建议参考资料:

N-flakes

N-flake(或 polyflake )是一种分形结构,它是用自身的多个缩放的副本去替换(通常按顶点和中心的对应位置放置)初始形状,然后对每个新形状递归重复这个过程。

递归

这是我们探索分形的一个很好的起点,因为它允许我们通过一个简单的动画节点设置来涉及递归和其他重要的概念。

这就引出了我们的 主要问题: 动画节点(目前) 不支持 纯递归。我们需要一些变通办法。一种选择总是依赖纯 Python 脚本,正如我在前一篇文章中所解释的那样,但我希望这篇文章的 重点 更多地放在动画节点上,因此我们可以通过迭代和循环队列来近似递归。

这个想法是依靠 Loop Input 重新赋值(Reassign) 选项来保持一个结果队列,然后我们可以在下一次迭代中处理它。

让我们考虑正多边形的 n-flake 情况。给定线段数 n (多边形的边数),半径为 r,圆心为 c,我们计算以 c 为中心的 n 个多边形的点,半径为 r。对于每一个新的计算点 p,我们重复这个过程(即找到以 p 为中心的多边形),但通过一些预定义的因子来调整半径 r 的大小。

  • n 多边形动画节点的设置

1_Gga3EyaAemWLVVCCYSReeA.gif

所有的主要逻辑都在下面的循环中,它负责计算多边形点和新的半径,并重新分配它,以便下一次迭代可以处理它们。理解这部分 非常重要: 在第一次迭代期间,循环(Loop)节点将处理传给调用子程序节点的任意值,而对于所有后续迭代,循环节点将处理在前一次迭代中更新的值。每次这些数据都将被清理,这就是为什么我们要维护两个队列(centers_queueall_centers),前者是我们还没有处理的中心点,后者是迄今为止计算的所有中心点的集合,它将被输出并被用作创建样条曲线列表。

  • 主循环与我们的缩放和重新赋值逻辑

image.png

image.png

这两个子程序都是 Python 脚本。在这些例子中,我发现编码更加简洁,不需要转换为纯节点。

计算一个正多边形的点:

points = []
# 计算每个给定中心的正多边形点
for center in centers:
    angle = 2*pi/segments  # 弧度
    for i in range(segments):
        x = center[0] + radius*cos(angle*i)
        y = center[1] + radius*sin(angle*i)
        z = center[2]  # z 值为常量
        points.append((x, y, z))
    #points.append(points[-segments])  #  close the loop

获取尺度因子:

from math import cos, pi
# 计算任意 n-flake 的比例因子 
# https://en.wikipedia.org/wiki/N-flake
cumulative = 0
for i in range(0, segments//4):
    cumulative += cos(2*pi*(i+1) / segments)
scale_factor = 1 / (2*(1 + cumulative))

额外的循环设置是将点分割,以便每个多边形转换为单独的样条。

image.png

转到 3D,规则多面体

我们可以很容易地调整这种设置来处理规则实体(Solid)。这一次,我们不再自己计算形状顶点,而是依靠场景中已经存在的对象,获取它的顶点并递归地转换它们。我们利用矩阵属性和 Compose Matrix 节点来最小化所需的工作量或节点。以下是设置

image.png

输出的 matrix_queue 用一个单位变换矩阵(没有平移、没有旋转和单位缩放)初始化,而我们的 transformation 列表是结合了任意缩放因子的目标/输入对象顶点位置,当用于变换另一个矩阵时,这相当于说,“将所有东西移到那里去,并按给定的因子缩放”。

这里采用了执行变换循环和重新分配操作的递归部分。

image.png

  • 柏拉图实体的样本结果如下(在四到五次迭代之间)

1_Gga3EyaAemWLVVCCYSReeA.gif

1_Gga3EyaAemWLVVCCYSReeA.gif

然后可以更新原始的平移矩阵,这样新的实体就不会完全放在前一个顶点上,而是放在经过前一个中心和新顶点的直线上的任何点上,如上边动画中所示。

以前的平移矩阵是纯粹的顶点位置,现在我们做一些向量数学操作来从原始位置移动这些点。如果我们用它们减去物体的中心,我们就得到了感兴趣的方向向量。然后我们可以将其除以任意值来相应地放置新的递归实体。

image.png

如果我们只实例化最后一次迭代的对象,我们得到适当的柏拉图实体分形。著名的例子是 Sierpinski 四面体和 Menger 海绵。

  • 在 Eevee 中呈现的示例,为了美观添加了布尔运算符

image.png

可尝试的事情

  • 跨递归步骤 任意/随机更改 n-flake 类型
  • 使用置换贴图来模拟额外的递归步骤,而不需要创建更精细的额外几何结构
  • 体积和纯着色节点设置

L-System

L-System(或 Lindenmayer System)是一种语法; 指定如何通过一组规则、字母表和初始公理生成文本字符串的东西。它还附带一个转换机制,将这些字符串转换为几何图形。

我们对 L-System 感兴趣,因为它们可以用来生成自相似的分形,也因为我们可以通过 L-System 节点轻易获得它们。下面是一个将结果实例化到对象的示例设置。

1_Gga3EyaAemWLVVCCYSReeA.gif

旁边的是由公理 X (初始状态)和两个规则 X=F[+X][-X]FXF=FF 定义的分形平面构型。节点负责为指定的代数逐步演进系统,它可以很好地将其划分为小部分,以便实现平稳过渡。它还能将文本结果转换为网格。因此,您必须遵循公理和字母用法的指定约定。

另一个需要考虑的 重要 参数是高级节点设置中的 Symbol Limit ,它指定生成的字符串的最大长度。如果超过此限制,系统将抛出错误。

例子

  • 龙曲线

1_cmYlFrlZiCj3d0e_kx4F7w.gif

dragon curve - axiom: FX, rules: (X=X+YF, Y=-FX-Y), angle: 90
  • 分形植物

1_Gga3EyaAemWLVVCCYSReeA.gif

fractal plant - axiom: F, rules: (F=FF-[-F+F+F]+[+F-F-F]), angle: 22.5
  • 西尔宾斯基三角形

1_Gga3EyaAemWLVVCCYSReeA.gif

sierpinski triangle - axiom: F-F1-F1, rules: (X=X+YF, Y=-FX-Y), angle: 120

可尝试的事情

  • 三维 L-system (如方位角+倾角)和随机 L-systems。(参见 Python 代码)
  • 步长规则,与增长成正比,这样视图可以保持不变,而增加其世代

曼德尔布罗特

作为分形之父,毫不奇怪,最常见和经常显示的分形都以他的名字命名。曼德尔布罗特集合与茱莉亚集合一起,为一种现象提供了理论基础,这种现象再次由简单的规则定义,但却能够产生一幅美丽和复杂的无限图景。

我们可以把这些集合看作是 “坐标形式的函数的迭代”,其中我们处理的是复数平面。Jeremy Behreandt 的这篇文章提供了一个令人印象深刻的详细介绍了复数、Mandelbrot 集以及更多东西,所有都以Blender 的 Python API 和开放着色语言(OSL) 编写。

我们完全可以在 Blender 节点中实现类似的结果(不需要脚本),但与往常一样,我们会受到设置中缺少迭代/递归功能的限制。基本的思想是在复数的平面上根据公式移动

image.png

根据(收敛到无穷大的)速度给每个点上色。这里 z 是起始点,c 可以是任意选择的复数。该公式可以重新写成纯笛卡尔平面坐标形式为

image.png

我们现在有 (x,y) 作为点,ab 作为控制值(之前 c 的实部和虚部)。

这里平移的公式在着色器节点设置

  • 坐标形式函数的节点设置

image.png

iterations_count 部分用于跟踪有效的迭代,即点没有偏离的迭代。为了实际目的,这可以通过检查向量的大小是否小于 2 来近似。

  • 曼德尔布罗特集的设置
    image.png

然后我们可以插入我们的纹理坐标,通过分离 xy,如图所示。对 ab 重用它们可以得到曼德布洛特集,或者我们可以传递自定义值来研究其他集和结果。

现在是 最糟糕 的部分: 我们必须通过非常不优雅的复制粘贴来近似函数迭代,对我来说,这看起来就像这样。100 次迭代是提供良好结果的一个不错的估计,你添加的越多,你得到的细节就越多,但设置很快就会变慢。

image.png

但至少我们现在有东西可以玩了

1_Gga3EyaAemWLVVCCYSReeA.gif

image.png

曼德尔球

曼德尔球是曼德尔布洛特集合的三维投影。Jonas Dichelle 已经有一个很棒的教程,解释了如何在 Blender 节点和 eevee 中模拟一个曼德尔球(Mandelbulb) 。鉴于该方法依赖于体积度量,因此值得参考Gleb Alexandrov 在 Blender 会议上关于 3D 星云的演讲

1_Gga3EyaAemWLVVCCYSReeA.gif

  • 调整下公式和颜色
    image.png

(完)

本文正在参加「金石计划 . 瓜分6万现金大奖」

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容