`
kidneyball
  • 浏览: 326906 次
  • 性别: Icon_minigender_1
  • 来自: 南太平洋
社区版块
存档分类
最新评论

分形绘图简介(5)分形火焰算法简介(一)

 
阅读更多
上一篇介绍了怎样用Apophysis从头创作一幅分形图。步骤虽然很简单,但估计一般人(例如我自己)看了都一头雾水,为什么这样设几个数字就画出来一幅画呢?这些无厘头的操作和最终结果有什么逻辑关系呢?要搞清楚其中的关联,就不得不去研究一下Apophysis所依赖的算法:The Fractal Flame Algorithm。也不知道中文有没有对应的专有译名,我把它叫作“分形火焰算法”(或者“分形燃烧算法”?)

这个算法不是什么高深的秘密,安装了Apophysis后就能在help里找到pdf文档的链接。也可以直接在这里下载。

不过老实说,我辛辛苦苦看完算法之后,才发现看懂了算法其实对绘画没有半点帮助……因此如果你没兴趣知道这些分形图的原理,应该可以不看算法直接开画,或者只看到三角变换的原理即可。只不过作为一个搞开发的人,不去刨根问底一下实在有点心虚。因此,本文的定位是,简单介绍算法原理,让我们在作画时对各种的操作的运作原理有个简单的印象。我尽量不去直译算法文档中的数学语言,用自己的话去描述。其实原文也不算复杂,想了解如算法和实现细节的话完全可以看原文。

那么下面就开始来看这个算法。

IFS系统
前面已经说过,分形图其实是把一个平面进行一系列函数映射所得到的结果。那么,这究竟是什么样的函数映射呢?这种映射名叫“迭代函数系统”(Iterated Function Systems,简称IFS)。一个在二维平面上的IFS方程是这样的:


* 这里的Fi是点集到点集的映射。用编程的语言来说:参数类型和返回类型都是点集。

换言之,有n个函数,我们希望在平面上找到一组点集S,使得S在经过这些函数分别变换后再取并集,得到的还是S本身(数学上这样的解称为函数的不动点)。而我们所看到的分形图,本质上,就是这个点集S。当然,要令这个S能实际存在,这些函数不能是完全随意的,它们必须能保证整个系统是收敛的。也就是说,把这个系统函数应用到整个平面,得到的结果再继续应用到这个系统中,若干次后,结果会越来越接近实际的不动点集。

实际求解算法
呃,似乎很抽象,不过不要紧,这个定义对我们作画一点用处也没有。实际上,要求解这个方程是很麻烦的。所以在分形火焰算法中,使用了下面这种近似的求解步骤:


* 这里的Fi是由点到点的映射。用编程的语言来说:参数类型和返回类型都是点。
* bi-unit square是指x和y都在[-1,1]之间的那个点集,也就是左上角坐标是(-1,1),右下角坐标是(1,-1)的边长为2的正方形内的所有点。

它的意思是,在bi-unit squre这个正方形点集里随机选取一个点(x,y),然后重复以下步骤:
1. 在n个函数中随机选一个Fi;
2. 用这个函数变换(x,y),得到新的点(x,y);
不停重复以上两步,并且在第20次迭代之后,把后续每次迭代的结果点画出来。

这种方式之所以可行,是因为根据前面的方程,如果(x,y)属于S,那么Fi(x,y)也肯定属于S。而既然整个系统是收敛的,以线性函数的平均收敛速度0.5为例,20次迭代之后,求得的点与实际的不动点的误差大约是10的-6次方,已经大大超出了作画时一个像素的精度。当然,实际使用的未必是线性函数,因此收敛速度也有所不同。好在我们现在不是考试,而是作画,具体的迭代的次数可以由用户去选定,如果次数不够,结果不够精确,图像也就不那么好看而已,而不会导致你高考落榜。

Variation
以上是整个系统的运作原理,为了便于在实际操作中描述这些变换函数,我们引入Variation的概念。在实际操作中,一个变换函数可以被描述为:



可以看出,这个Fi函数接受了一个点(x,y)作为参数后,先对其做了一个线性变换:
x' = ax + by + c
y' = dx + ey + f
如果我们高中解析几何还没全忘掉的话,应该记得对一个形状(点集)进行这种变换包括了缩放,旋转,拉伸和平移等操作。

然后,把这个变换后的点传入一个Vj函数中再作变换。而这个Vj函数,就是我们之前看到在Apophysis的Variation标签页中列出的那些变换函数(当然,这些函数必须能保证IFS的收敛性)。分形火焰算法中列举了49个基本的Variation函数,在Apophysis中,还可以通过安装plugin的方式增加新的Variation。

这些Variation函数被设计为能对一个形状的点集进行某种有特定含义的变换。例如线性变换(linear),球面变换(spherical),漩涡变换(swirl)等等,并且根据其含义起了名称,方便我们选用。在算法文档中列出了49个基本Variation的方程,在这里选几个例子看看:







可以看到,linear是最简单的变换(就是变成原来的样子)。不过由于Fi函数在把参数传进Vj之前就作了一次线性变换,所以实际上对于整个Fi来说,就是普通的线性变换。

另外,我们在上面的JuliaN变换中看到,这个方程除了使用x,y和一些常量之外,还使用了juliaN.power和juliaN.dist两个额外的参数(除了这两个参数之外,式子中的其他符号都是常量或者随机数),这些额外的参数就称为Variables(变量)。如果搞过函数式编程,应该对“自由变量”这种说法不会陌生。这些额外的参数就是自由变量,对于没玩过函数式编程的朋友,只要知道可以在Apophysis的Variable面板指定这些额外的参数的值就行了。

变换三角
有了这些背景知识,现在我们可以来看看Apophysis里的变换三角究竟是什么一回事。其实很简单,上面说到,每个Fi都先对传入的点进行了一次线性变换,而这个线性变换涉及到6个参数:a,b,c,d,e,f。所谓变换三角,就是这6个参数的形象描述。

准确来说:
1. 坐标原点到变换三角的O顶点的矢量代表了c和f。也就是线性变换的平移量。
2. 变换三角的O顶点到X顶点的矢量代表了a与b。
3. 变换三角的O顶点到Y顶点的矢量代表了d与e。
其中a代表了在X方向上的缩放量,e代表了在Y方向上的缩放量。而当b,c不为0时,形状出现拉伸和旋转。

也就是说,Transform面板上的输入数值,分别代表了:



例如上面的原始变换三角,如果Variation取linear(线性变换)时,该变换无位移(O顶点为(0,0)),无缩放(X1即a为1,Y2即e为1),无旋转和拉伸等(X2和Y1即b,c均为0)

由于Variation中的linear的默认值是1,默认使用此变换。此时Apophysis的主窗口预览中显示一个由噪点组成的正方形。这些噪点就是前面的求解算法中所提到的随机选取的点。(对于这个原始变换,这些点本身就是解)

现在我们把变换三角稍微缩小一点。把Transform参数设为:
X:0.999 ,0
Y:0 ,0.999
O:0,0

图像就变成了:



这是什么回事,为什么不是一个小一点的正方形,而是这样的放射线呢。对照前面的算法就明白了,对任意一个随机点,将反复应用这个变换(x和y分别乘以0.999),使得这个点不停向原点靠拢。其实对于这个变换,原点才是真正的不动点。但由于收敛速度太慢,超过了20次迭代后还没有靠近原点,因此每个点收敛的轨迹被画出来了,变成了放射线。

如果我们把三角变换缩小得再稍微多一点,例如把0.999设为0.9,就能看到这个图形都不见了,变成了原点上的一个点。

同样,假如我们在某个方向上缩小。例如设为:
X:0.9 ,0
Y:0 ,1
O:0,0
可以看到,图像变成了Y方向上的一条直线。

从这里可以看出一个规律:如果映射之后的图像比原来的图像在某个方向上缩小了(就是在某个方向上不能达到原来图像的边界),那么在这个方向上的图像就会最终缩小成一点。这个规律对理解下面的示例比较重要。

作图实例1,塞宾斯基三角
明白了这些基础知识后,我们不妨通过一个实例来看看这个迭代系统是怎么起作用的。

先来一个最简单的例子,画一个赛宾斯基三角形。这个三角形的特点是,先作一个大三角形,然后取各边中点连成一个小三角形,挖去这个小三角形。然后在剩下的三个小三角形里重复这个步骤。然后再在剩下的三角形里重复这个步骤……最后就得到了:



那么在Apophysis怎么去画呢?当然,如果我们是数学家的话,直接写一堆方程解出所需的abcdef就行了。但我们现在是画家,能不能用点直观的方式去想出来呢?

用惯photoshop的人通常的思路是:Ok,第一步首先要想办法画出一个三角形来。不过,在分形里可不是这样玩的。虽然现在我们手上有一个正方形(原始变换),但它的形状其实不重要,因为无论它最初的形状是怎样的,迭代变换之后的不动点都是一致的。是我们选用的迭代函数决定了最终的图像,而跟初始形状没有关系。那么我们就应该只关注整体与局部的关系。

仔细来看看这个赛宾斯基三角形,可以发现它整体与局部的关系是:整体在高度和宽度都缩小一半之后,复制了三份,两分并排列在下面,一份叠在上面。从前面的收敛规律来看,这样的变换排列方式保证了下方最宽的地方与原图像宽度一致,而高度也与原图像一致。但上方由于只有一份,在横向上宽度比原图像缩小了,最终将缩小为一个点。这不就是个三角形吗?看来有戏,现在就开始在Apophysis里试试看。

首先新建一个变换三角。因为Variation默认就是linear,可以不管。把它缩小一半。

在Transform里设置:
X:0.5, 0
Y:0 , 0.5
O:0, 0

可以看到,此时图像变成了一个小点。然后选中这个变换三角,按Insert键复制一个。把Transform设为:
X:0.5, 0
Y:0, 0.5
O:0.5, 0

也就是原图像缩小一半后,在下方并排两个副本。不出所料,图像变成了一条直线。(现在新变换在横轴方向大小与原图像一致,但在纵轴方向缩小一半,最终收敛到了横轴上)

再选中任意一个三角,按Insert键复制,把它平移到上端:
Transform设为:
X:0.5, 0
Y:0, 0.5
O:0.5, 0.5

立杆见影,结果出来了:



如果你还没有想清楚,我们把这个迭代过程总结一下:

* 这里用三个变换,每个都把原来的图像(不管是啥)缩小一半。

* 通过位移,在原来图像的范围内,下方并排两个,上方叠一个。

* 在这个迭代变换中,每一步原来的图像都先进行这样缩小排列,然后得到新的图像再进行这样的缩小排列。

* 到最后,整个图像收敛成了这个叠出来的三角形的形状,而且其中每一部分也是这样的三角形。

我们不妨把局部放大一下,可以看到细节里也是这样的三角形:



……随便写写就1点多了。看来要分两集了。看了每屏的字,来点干货提提神。昨天试画了一幅,貌似还不错。



代码是:


<flame name="Blank Flame" version="Apophysis 7x Version 15 (High-Memory Version)" size="1500 1000" center="0.0936657854738676 0.0140814830125901" scale="136.670692054355" oversample="1" filter="0.5" quality="50" background="0 0 0" brightness="8.1695652173913" gamma="1.85" vibrancy="1.44" estimator_radius="9" estimator_minimum="0" estimator_curve="0.4" enable_de="0" plugins="" >
   <xform weight="1" color="0.189" blur="0.4" coefs="1 0 0 1 0 0" opacity="1" />
   <xform weight="0.5" color="0.009" linear="1" julian="1.5" coefs="-1 0 0 -1 0 0" julian_power="8" julian_dist="1" opacity="1" />
   <xform weight="1.5" color="0" julian="1" coefs="1 0 0 1 -1.5 0" julian_power="12" julian_dist="-1" opacity="1" />
   <xform weight="0.75" color="0.038" spherical="1" curl="0.5" coefs="1.25 0 0 1.25 0 0" curl_c1="0" curl_c2="1" opacity="1" />
   <xform weight="0.5" color="0" juliascope="0.25" coefs="0 1 -1 0 0.5 0" juliascope_power="12" juliascope_dist="-1" opacity="1" />
   <palette count="256" format="RGB">
      FEEA31FEEA30FFEA2FF0C124E29919D39524C4922FF9B274
      F3C07FEDCE8BEFCF6DF2D150F3E356F4F55CF8F632FAE72E
      FDD92BE0D241C3CC57B3C660A4C069C47066FC432EA22801
      82140D63001955092348132D4D293A533F48B28395B74675
      BC0A56DC0641FC032DFB0124FA001BEE0000E0001281010E
      50062D1F0C4C0F0929000706001C0300310075A706AAA00D
      DF9915E68426EE6F38D96E46C46E55A34128A241001A3B28
      0D4A15005A0200590201590202311F000D2C000024000021
      00001F00011C00021900031C00041F00002201002400092C
      00142F001F33002C37003A3B00483D015643023634032C1A
      052301021E0A001913002224022A36003638005643116567
      56848A9BA4ADC298B3E98CB9C0968AABA38C8AB88A77AA60
      649D366CA22B75A7208BB40EA4BE1C94BD0992B5037B8701
      79610F773C1E632A1F2D27111B1133010A2B010022000424
      0009270018250028241C6410436E03907734DA9460DAC380
      D5C181D0C0829CB25F9B976A95A986D47E97FF055CFF057C
      FF059DED34A3DC63AAEC7792F3A49DF4A59EFD9B82FD0559
      FD0559FE0559F30040E80028DF0400C50F02450023311B1A
      1E3612025C02007402017604006414016622006545024325
      01542101651D2A83034A88032F8200007502005605003F12
      002820000A2A000B2B00213129472F79942992B620C5BD1D
      FEEB2DFDFB37FAF444F4F45EF8FA95BCD39D6DAA9B6AA692
      68A289327E720360560255450235382D1B316D0056B80058
      D7013DF0003DEE0E19EE0E0CE85004C56F009EAA0CB1B642
      C4A433FC7440F06745FA4331FB291AFA3F14FA3F16ED5724
      C7861E8B884189905C89674E583136404407414401648200
      88A70CA2BF2FD6E621F2F333FAF170F3F89ADBDFBED1DCB4
      B0CDAFB7C2BCF2CBE0F5EAE8FAF6D1E5EFE7ECE1E7DBE6D6
      BFD3B8A4C495829E886EA3996EA79E6A9790347D6A294546
      00453D003839013236011E2E0024320134370F3E2C015410
      2B480E4035082B2B0F350D01420123EC0089810E5B1B0045
      44004D6D0055992C34C55813D36C10E1800DF3BD29F8D32D
   </palette>
</flame>
  • 大小: 11.9 KB
  • 大小: 32 KB
  • 大小: 16.4 KB
  • 大小: 50.7 KB
  • 大小: 54.1 KB
  • 大小: 46.2 KB
  • 大小: 20.6 KB
  • 大小: 12.6 KB
  • 大小: 10.1 KB
  • 大小: 98.7 KB
  • 大小: 51.9 KB
  • 大小: 2.5 MB
1
1
分享到:
评论
2 楼 lliiqiang 2014-05-30  
分形维数以距离增加时个数增加指数为标准,相当于通常所说的测量先定好标准比例:以线为1维.
1 楼 fengda2870 2012-01-06  
我建议你把学习笔记也转到分形艺术网

相关推荐

    Delphi递归法绘图分形大树Tree及节点.rar

    Delphi绘图程序实例:绘制大树Tree节点,采用递归分形算法画一个树,点击一次画一次,画出支杆,代码有参考性。

    Fractal Producer 分形图像生成器6.06版——含源代码

    最新添加“高精度Julia”和“高精度Mand”算法,突破10的27次方放大倍数极限,现在很轻松到50次方倍,只是运算速度很慢,所以一般情况建议用 普通的算法,只是在出现马赛克时才用高精度算法 添加更多参数“广义MJ...

    二维IFS迭代函数系统分形绘图

    内容索引:VC/C++源码,算法相关,二维,分形绘图,函数 二维IFS迭代函数系统分形绘图,看似树叶,实际上运用了很多算法才生成的图形。程序功能:改变视图、颜色设置、绘制命令、参数设置等,可以绘图进行一些自定义。

    fractals:分形主要使用javascript和html5 canvas实现。 图的深度优先遍历和宽度优先遍历的示例。 面向对象的设计

    分形 该项目旨在实施和调查树木/图形/分形。 执行 当前,所有代码都是在jQuery库的帮助下使用javascript完成的。 渲染是使用HTML5和Canvas元素完成的。 程序是使用面向对象的设计和MVC模式的变体来实现的,以将模型...

    计算机图形学原理及算法教程(cd).rar

    4.3 简单CAD绘图系统功能简介 103 4.3.1 简单CAD绘图系统运行界面 103 4.3.2 简单CAD绘图系统功能 103 第5章 三维图形变换 107 5.1 三维图形几何变换矩阵 107 5.2 三维图形基本变换矩阵 107 5.2.1 平移变换 107 ...

    matlab绘图的形状代码-koch-for-fitting:该项目是Koch分形曲线的实现,用于生成算法,该算法将有助于解决增材制造中普遍存

    matlab绘图的形状代码增材制造的Koch实施 程序文件已完全包含,并已实现以用于测试用例场景中的验证。 该文档应提及用户可用来访问该程序的好处的方法,从而可以根据自己的意愿以任意形状制作G代码。 遵循的步骤: ...

    图形图象类代码.zip

    贝赛尔纽曼函数绘图代码,产生水纹、火焰及熔岩效果示例, 3DS动画文件阅读器源代码,JPEG处理源代码 ,图象腐蚀,膨胀,细化算法,图象的检测,模板匹配算法代码,JPEG压缩编码,图象的几何变换算法,图象平滑处理...

    IFS算法支持的绘图程序

    本人大学MFC课程设计的作品。提供完整的图片载入、绘制、保存功能,能更改画笔粗细。能画出彩虹的效果。同时使用了IFS算法对分形的模拟,相当有趣。内含静态汇编的文件可直接使用。

    图形处理类的实例40个

    指定一种颜色把一幅位图变透明 字幕滚动源代码"&gt;包括对图像进行离散余弦变换处理的源代码 对图像进行水平镜像处理的源代码 反走样 Line 对图像进行中值滤波处理的源代码 计算机图形学立方体旋转 更新版 分形图像制作...

    meng_v71.zip_PCA绘图

    是学习PCA特征提取的很好的学习资料,分形维数计算的毯子算法matlab代码,包括数据分析、绘图等等。

    计算机图形学pdf(可copy内容版)

    4.3 简单CAD 绘图系统功能简介 4.3.1 简单CAD 绘图系统运行界面 4.3.2 简单CAD 绘图系统功能 第5章三维图形 5.1 三维图形几何变换矩阵 5.2 三维图形基本变换 5.2.1 平移变换矩阵 5.2.2 比例变换矩阵 5.2.3 旋转变换...

    计算机图形学基础教程(Visual C++版)——第6~10章

    书籍目录: 第1章 导论  1.1 计算机图形学的应用领域  1.1.1 计算机辅助设计 ...第5章 二维变换和裁剪 第6章 三维变换和投影 第7章 自由曲线和曲面 第8章 分形几何 第9章 动态消隐 第10章 真实感图形 附录 参考文献

    计算机图形学基础-pdf.zip

    第5章基本图形生成算法 5.1直线的扫描转换 5.1.1数值微分法 5.1.2中点Bresenham算法 5.1.3Bresenham算法 5.2圆的扫描转换 5.2.1八分法画圆 5.2.2中点Bresenham画圆算法 5.3椭圆的扫描转换 5.3.1...

    Mandelbrot集和Julia集的图像绘制

    采用简单的算法绘制Mandelbrot集和Julia集的图像,参数可以自己设置

    Python 科学计算

    1.1 Python 简介......................................1 1.2 安装软件包......................................2 1.2.1 Python(x,y)..................................... 2 1.2.2 Enthought Python ...

    Matlab2014软件教程(完美版).pdf

    4、Matlab 绘图命令.......................................................................... 17 5、Matlab 在高等数学中的应用...................................................... 34 6、Matlab 在线性...

Global site tag (gtag.js) - Google Analytics