什么是Mandelbrot集合?

Mandelbrot集合是在复数平面上组成分形的点的集合,它正是以数学家Mandelbrot命名。

Mandelbrot集合可以用复二次多项式

\[f_c(z)=z^2+c
\]

来定义

其中c是一个复数。对于每一个c,从\(z = 0\),开始对\(f_c(z)\)进行迭代。

序列\((0, f_ c(0), f_c(f_ c(0)), f_ c(f_ c(f_ c(0))), \ldots)\)的元素的模(复数具有模的概念)或者延伸到无穷大,或者只停留在有限半径的圆盘内。Mandelbrot集合就是使以上序列不延伸至无限大的所有c点的集合。

从数学上来讲,Mandelbrot集合是一个复数的集合。一个给定的复数c或者属于Mandelbrot集合M,或者不属于。比如,取c = 1,那么这个序列就是(0, 1, 2, 5, 26, ...),显然它的值会趋于无穷大;而如果取c = i,那么序列就是(0, i, -1+i, -i, -1+i, -i,...),它的值会一直停留在有限半径的圆盘内。

事实上,一个点属于Mandelbrot集合当且仅当它对应的序列(由上面的二项式定义)中的任何元素的模都不大于2。这里的2就是上面提到的“有限半径”。

绘制Mandelbrot集合

可以将屏幕上的一个像素映射为坐标系中的一点,如果该点属于Mandelbrot集合,就将该像素着为黑色,这样逐一对每个像素进行判断和着色,就可以模拟绘制Mandelbrot集合了。

完成映射后来考虑如何判断一个点是否属于该集合。其根据就是上面的结论:一个点属于Mandelbrot集合当且仅当它对应的序列(由上面的二项式定义)中的任何元素的模都不大于2,由于序列的的元素有无穷多个,我们只能取有限的迭代次数来模拟了,比如取100或1000次。

下面的代码shader代码完成了上面的思想。

其中迭代次数为200.

fragCoord.xy传入的当前要计算颜色的像素点的坐标。iResolution.xy是显示区域的宽高。iTime是时间变量。

代码的前面一部分将像素点坐标转换到了逻辑坐标系的坐标,并且随着时间变化对显示区域不断缩放,使得我们可以动态的观察不同大小尺度的Mandellbrot集合。

后面一部分计算了当前点是否位于Mandelbrot集合。

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 p = -1.0 + 2.0 * fragCoord.xy / iResolution.xy;//[0, 1]
p.x *= iResolution.x/iResolution.y;//keep with h/w radio // animation
float tz = 0.5 - 0.5*cos(0.225*iTime);//[0,1]
float zoo = pow( 0.5, 13.0*tz );//[1/2^13,1]
vec2 c = vec2(-0.05,.6805) + p*zoo; // iterate
vec2 z = vec2(0.0);
float m2 = 0.0;
int isIn = 1;
for( int i=0; i<200; i++ )
{
if( m2 > 2.0 ) { isIn = 0; break; } // Z -> Z² + c
z = vec2( z.x*z.x - z.y*z.y, 2.0*z.x*z.y ) + c; m2 = dot(z,z);
} vec3 col = vec3(isIn==1?0:1 ); fragColor = vec4( col, 1.0 );
}

可以进入这个地址观看效果:https://www.shadertoy.com/view/3lsXWH

我们已可以从可视化的效果中观察到数学的美妙,从简单的递归定义中可以得的这种在大小尺度上不断重复的图案。

然而渲染效果不够好,我们观察到很多黑色的散点,和主体并不是连接的,而且在缩放的过程中,这些散点不断忽然消失,又忽然出现。

改进渲染效果

前面一个假设“将屏幕上的一个像素映射为坐标系中的一点”其实是不对,屏幕中的像素是有限的,一个像素点对应的应该是坐标系中一块区域,而不是一个点,然而计算中将像素点映射到了对应区域的中心点,如果这块区域位于Mendelbrot集合分形图形的边缘分支上,它包含了无穷的分形的细节,区域中心点可能刚好位于Mandelbrot集合中也可能刚好位于集合外。所以在分形图形的边缘分支处出现了很多的散点。随着缩放,在细节丰富地方的黑色点出来类似噪声的效果,其实是由于前后两帧像素点对应的区域的中心位置是否在集合中发生了变化。



如何改进渲染效果呢?应该让像素反映出改点对应区域的密度,而不是仅仅取中心点计算,即如果该区域内有更多比例的点位于集合,使它表现出来的颜色更趋于黑色。

但是如何计算出对应区域的位于集合内的点的比例?想要得到这个精确的数值貌似是个不可能的任务,因为在我们的认知中分形图形并不是一个确定性的图形,它具有无穷的细节。但是我们可以采用某种方法估计这个比例。

假设我们可以得到某一点距离Mandelbrot集合图形边界的最短距离Dist,如果点位于集合内,Dist等于0,如果位于集合外,Dist是一个正值。Dist的计算方法在Inigo Quilez的文章distance rendering for fractals中给出了,在本文中就不具体阐述来了,我们把它当做一个黑盒,假设它的结果是正确的。

如果最短距离大于像素区域的大小,说明没有像素区域内不包含集合中的点,该区域的密度为零;

如果最短距离小于像素区域的大小,可以说明以像素区域中心为中心以最小距离为半径的圆内一定不包含集合点,我们假设剩余的面积都是被集合点充满,然后我们可以根据面积的比例来估计集合点的比例。如下图所示:



像素区域的宽高分别为w,h,中心点距离集合边缘的距离为D,则区域内密度估计为\(Density = (w*h - Pi*D^2)/(w*h)\)。

这个估计值显然会比真实值更大。

当图像缩小时,像素对应的区域会变大,如图中的大的红色框所示,这时的密度估计会变大,在渲染中表现就是随着视口放大,原先有很多细节的地方逐渐缩小,并且变得更黑。

下面是根据上面讨论实现的效果:

观看地址:https://www.shadertoy.com/view/wtlSW8



从shaderToy的实时演示和截图中可以看出显示效果得到了很大提高,如果有更好的密度估计方法,显示效率和显示质量还能得到进一步提升。

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 p = -1.0 + 2.0 * fragCoord.xy / iResolution.xy;
p.x *= iResolution.x/iResolution.y; // animation
float tz = 0.5 - 0.5*cos(0.225*iTime);
float zoo = pow( 0.5, 13.0*tz );//[1/2^12,1]
vec2 c = vec2(-0.05,.6805) + p*zoo; // iterate
bool isIn = true;
vec2 z = vec2(0.0);
float m2 = 0.0;
vec2 dz = vec2(0.0);
for( int i=0; i<200; i++ )
{
if( m2>1024.0 ) { isIn=false; break; } // Z' -> 2·Z·Z' + 1
dz = 2.0*vec2(z.x*dz.x-z.y*dz.y, z.x*dz.y + z.y*dz.x) + vec2(1.0,0.0); // Z -> Z² + c
z = vec2( z.x*z.x - z.y*z.y, 2.0*z.x*z.y ) + c; m2 = dot(z,z);
} // distance
// d(c) = |Z|·log|Z|/|Z'|
float d = 0.5*sqrt(dot(z,z)/dot(dz,dz))*log(dot(z,z));
if (isIn)
d = 0.0; // estimate density in the pixel area
vec2 pixelScale = vec2(zoo/iResolution.y*iResolution.x/iResolution.y,zoo/iResolution.y);
float pixelArea = pixelScale.x * pixelScale.y; float k = 1.0;
float density = clamp((pixelArea - 3.1415926*d*d*k)/pixelArea, 0.0, 1.0); density = pow(density,3.0);
vec3 col = vec3( mix(1.0,0.0,density) ); fragColor = vec4( col, 1.0 );
}

其中变量d就是当前像素点代表的区域中心点距离集合边界的距离,其计算原理本文暂且不谈。

vec2 pixelScale = vec2(zoo/iResolution.y*iResolution.x/iResolution.y,zoo/iResolution.y);

这行代码根据当前的缩放比率以及显示分辨率计算了像素代表的区域的大小。

float density = clamp((pixelArea - 3.1415926*d*d*k)/pixelArea, 0.0, 1.0);

这行代码根据上文讨论计算了估计密度。

然后剩余代码根据估计密度计算出了像素的灰度值,这部分代码可以更加感性的进行调整,只要能获得更好的显示效果。

着色

上面实现的效果是一张灰度图,如果我们根据某种规则为像素赋予颜色,也许可以获得更加具有美感的显示效果。

todo

Mandelbrot集合及其渲染的更多相关文章

  1. OpenMP并行化实例----Mandelbrot集合并行化计算

    在理想情况下,编译器使用自动并行化能够管理一切事务,使用OpenMP指令的一个优点是将并行性和算法分离,阅读代码时候无需考虑并行化是如何实现的.当然for循环是可以并行化处理的天然材料,满足一些约束的 ...

  2. 曼德勃罗(Mandelbrot)集合与其编程实现

    一.从科赫雪花谈起 设想一个边长为1的等边三角形(例如以下图所看到的).取每边中间的三分之中的一个,接上去一个形状全然类似的但边长为其三分之中的一个的三角形,结果是一个六角形.如今取六角形的每个边做相 ...

  3. 广义mandelbrot集,使用python的matplotlib绘制,支持放大缩小

    迭代公式的指数,使用的1+5j,这是个复数.所以是广义mandelbrot集,大家能够自行改动指数,得到其它图形.各种库安装不全的,自行想办法,能够在这个站点找到差点儿全部的python库 http: ...

  4. Mybatis:解决调用带有集合类型形参的mapper方法时,集合参数为空或null的问题

    此文章有问题,待修改! 使用Mybatis时,有时需要批量增删改查,这时就要向mapper方法中传入集合类型(List或Set)参数,下面是一个示例. // 该文件不完整,只展现关键部分 @Mappe ...

  5. OpenCV基础知识介绍

    1.图像与矩阵 一般来说,图像是一个标准的矩形,有着宽度(width)和高度(height).而矩阵有着行(row)和列(column),矩阵的操作在数学和计算机中的处理都很常见且成熟,于是很自然的就 ...

  6. 人工智能系统Google开源的TensorFlow官方文档中文版

    人工智能系统Google开源的TensorFlow官方文档中文版 2015年11月9日,Google发布人工智能系统TensorFlow并宣布开源,机器学习作为人工智能的一种类型,可以让软件根据大量的 ...

  7. Java Math 类中的新功能--浮点数

    Java™语言规范第 5 版向 java.lang.Math和 java.lang.StrictMath添加了 10 种新方法,Java 6 又添加了 10 种.这个共两部分的系列文章的 第 1 部分 ...

  8. 【转】Directx11 SDK文档

    原文地址:http://blog.csdn.net/cmt100/article/details/6343274 总结 这是一个初步的教程.我们将通过必要的步骤来创建一个Win32 Applicati ...

  9. Vulkan Tutorial 23 Descriptor layout and buffer

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 我们现在可以将任意属性传递给每个顶点的顶点着色器使用.但是 ...

随机推荐

  1. python 中 __dict__函数的使用

    class F: def __init__(self, name, age): self.name = name self.age = age obj = F('tom', 20)s = obj.__ ...

  2. Linux 部署 Django 系统

    一:安装uwsgi pip3 install uwsgi 二:进入项目目录下,创建uwsgi.ini配置文件 [uwsgi] # 使用nginx连接时使用功能,上线时才使用socket,指定项目执行的 ...

  3. 读取 .properties文件到数据库

    最近有一个这样的需求,将原本配置文件 .properties文件改到数据库当中,这样不用每次修改都重启服务器 java自带有处理 .properties文件的专有类 Properties,处理也很不错 ...

  4. 添加 @ResponseBody出现错误的问题

    maven配置: <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>ja ...

  5. Squirrel GUI+ Phoenix 连接Hbase

    一. 参考 http://blog.csdn.net/maomaosi2009/article/details/45598823 二. 问题解决 >Squirrel Client Connect ...

  6. 安卓的一些UI美化框架的使用

    目录 一.前言 二.Android-Bootstrap 三.Sweet Alert Dialog 四.ExplosionField 一.前言 在这里记录一些用到过的觉得还算不错的UI第三方开源美化框架 ...

  7. LeetCode | DP专题详解

    221 medium     221. Maximal Square Medium Given a 2D binary matrix filled with 0's and 1's, find the ...

  8. Angular build编译内存溢出"JavaScript heap out of memory"的解决办法

    关于最近使用angular build编译打包的时候,遇到内存溢出的突发情况,做一个简单记录 编译报错如下↓↓↓ 报错信息很直观地指出是内存溢出了.是什么导致了内存溢出呢?其根本原因在于 nodejs ...

  9. ip速度检测与云主机|VPS的抉择:bandwagonhost digitalocean hostWind Vultr Linode

    最近的梯子断了,网站又被注销了.又到了挑vps的时间了.其实, 这些东西,烦死人了.挺浪费生命的. 首先速度测试, MTR测试 网站速度测试 17CE. http://tool.chinaz.com/ ...

  10. Tableau常用图表

    条形图: 饼图: 调整大小: 折线图: 面积图: 组合图: 文本表: 突出显示表: 直方图: 气泡图: 散点图: