本文已参与掘金创作者训练营第三期「高产更文」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力。
例子 2 —— 树木
L-Systems 非常擅长对树木和植物进行建模。 让我们从这个例子开始:
lsysAxiom("[B]");
lsysRules("B=A[-B][+B]");
lsysDrawSymbols("AB");
lsysAngle(20);
lsysLength(length);
lsysDir(0, -1);
lsysIterations(iterations);
lsysAdvance();
这里我们引入两个新的符号: ‘[’ 和 ‘]’。当遇到左括号时,系统的状态会被保存(或推入)到堆栈中。当遇到右括号时,会弹出堆栈顶部的元素来恢复该状态。
系统状态由以下几部分组成:
- 笔的位置
- 方向
- 线的长度
- 线厚度
- 旋转角度
这使得我们可以轻松地创建分支。在这个例子中,我们从一个分支开始,它在每次迭代之后都会发芽两个新的分支。以下是经过 4 次迭代后的系统:
但真正的树枝通常会变得越来越小。我们如何用 L-System 来模拟这个呢? 这里有一个方法:
lsysRules("B=A[-B][+B],A=AA");
添加 A=AA 规则将导致每个分支在系统的每次迭代中都增加一倍大小。以下是经过 4 次迭代后的结果:
但是,如果我们希望分支以较慢的速度增长,而不是在每次迭代时加倍呢? 您还有两个选项: ‘%’ 符号,它将长度除以长度缩放因子(默认设置为1.3),以及 ‘&’ 符号,它将通过长度增加因子(默认设置为0.1)来减少长度。您可以通过调用 lsysLengthScale 和 lsysLengthInc 函数来更改这些值。
下面是一个例子规则,它将缩放因子应用于左侧分支,将递增因子应用于右侧分支,所以你可以看到它所产生的区别:
lsysRules("B=A[%-B][&+B]");
现在让我们让这棵树看起来更自然一点,通过添加一个中心分支,并在分支时减少厚度。为此,我们使用 ‘!’ 符号:
lsysRules("B=A[!%-B][!%+B]!%AB");
这是它经过 6 次迭代后的样子。现在看起来好多了!为了使其正常工作,不要忘记在美术应用的笔刷设置中绑定画笔的压力与线条粗细。
如果你画这个,你会注意到它花了更长的时间,因为现在每个分支都有 3 个分支。我们可以让树看起来更自然,通过随机跳过一些树枝来减少一些绘制时间。我们用 ‘?’ 符号。当遇到这个符号时,以一定的概率跳过它之后的指令(或分支)。绘制一个 0 到 10 之间的随机数,如果它高于当前的跳过数,则跳过该指令。在 ‘?’ 符号之前,我们通过简单地在规则字符串的某个地方写一个数字来设置跳过数。这里有一个例子:
lsysRules("B=A7?[!%-B]8?[!%+B]!%AB");
我们在这里做的是给左分支 10 次中有 7 次机会被画出来,给右分支 10 次中有 8 次机会。既然我们已经在 L-System 中引入了一些随机性,那么我们绘制的每棵树都会略有不同! 以下是我们在 6 次迭代后可以得到的结果:
去除完美的对称使事物看起来更自然。让我们添加更多随机性吧! 通过以下功能,我们可以让系统在旋转和绘图时给角度和长度加上一些随机值:
lsysAngleRandom(angleRandom);
lsysLengthRandom(lengthRandom);
这将使两个新的滑块出现。将 angleRandom 的范围设置为[0-45] (因为它是一个角度),你可以将 lengthRandom 的范围设置为[0-1] (因为它是当前长度的一个百分比)。小的数值将会使树看起来更真实。如果你把这些设置得太高,你会得到一些非常疯狂的结果,可能根本就不像树了。
这里有一些例子,你可以在 6 次迭代后得到什么,angleRandom 设置为 5 左右,lengRandom 设置为 0.15。我还将 Length Scale Factor 设置为1.6,以模拟树枝末端的叶子。