找不到按照我的目标为 Mandelbrot-set 上色的方法

Mor*_*S42 5 java math processing fractals mandelbrot

我已经成功地为 Mandelbrot 集着色,尽管在它变得“模糊”并且图案停止之前我无法放大很远。我通过增加 max_iteration 来解决这个问题,这是有效的,但我在 *1 放大倍率下得到的颜色很少,而且只有在放大时才会出现很多颜色。我理解为什么会发生这种情况,因为在“真正的”Mandelbrot-set 中没有颜色和增加 max_iterations 只会让它更接近那个。但我的问题是,在整个缩放过程​​中,像 youtube 上的缩放如何拥有美丽的色彩,同时仍然能够放大以获得永远的感觉?

我试过在网上到处寻找,但我找不到解决方案,当我查看这些 youtube 缩放的描述时,他们似乎几乎没有提供关于他们如何进行缩放的任何信息。

这里只是绘制 Mandelbrot 集的代码部分。下面的代码是在处理过程中编写的,它是带有添加视觉库的 java。您可以在此处了解有关该计划的更多信息:https : //processing.org/

//m is max_iterations
//mb is the function which calculates how many iterations each point took to escape to infinity. I won't be including the function since I know it works fine and it's quite messy.
//i'm using a HSB/HSV system to draw the mandelbrot

hue=(mb(x, y, m)*360)/m;
sat=255;
if (mb(x, y, m)<m) {
  val=255;
} 
else {
  val=0;
}

stroke(hue,sat,val);
point(x, y);
Run Code Online (Sandbox Code Playgroud)

我明白为什么我的问题会发生,但我不知道如何解决它。

这是一张具有低 max_iterations 并缩小的图像,您可以看到它非常丰富多彩:
低最大缩小

这是一张具有低 max_iterations 并稍微放大的图像,你可以看到它很无聊而且不是很丰富多彩:
low max 稍微放大

这是一张具有高 max_iterations 并缩小的图像,您可以看到它不是很丰富多彩:
高最大值缩小

这是一张具有高 max_iterations 并放大的图像,您可以看到它非常丰富多彩:
高最大值放大

Spe*_*tre 6

先来看看这个相关的QA:

主要思想是使用直方图将颜色梯度更有效地分配给已使用的索引,而不是在未使用的索引上均匀浪费许多颜色。它还使用特定的视觉上令人愉悦的渐变功能:

其他人建议的动态最大迭代次数只会影响缩放中的整体性能和细节。但是,如果您想要没有缩放的漂亮颜色,那么您需要计算浮点迭代计数,也称为Mandelbrot Escape。有一种数学方法可以从方程的最后一个子结果计算迭代计数小数部分。有关更多信息,请参阅:

然而,我从未尝试过,所以带着偏见阅读这篇文章:如果我读对了,你想要的是计算这个方程:

mu = m + frac = n + 1 - log (log  |Z(n)|) / log 2
Run Code Online (Sandbox Code Playgroud)

n您的迭代次数在哪里,Z(n)是您正在迭代的方程的复杂域子结果。所以现在计算mu浮点的颜色,而不是从n......

[Edit2] GLSL mandelbrot 与基于上面链接的分数转义

我添加了分数转义并修改了直方图多通道重新着色以匹配新输出......

顶点:

mu = m + frac = n + 1 - log (log  |Z(n)|) / log 2
Run Code Online (Sandbox Code Playgroud)

分段:

// Vertex
#version 420 core
layout(location=0) in vec2 pos;     // glVertex2f <-1,+1>
out smooth vec2 p;                  // texture end point <0,1>
void main()
    {
    p=pos;
    gl_Position=vec4(pos,0.0,1.0);
    }
Run Code Online (Sandbox Code Playgroud)

CPU端C++/VCL代码:

// Fragment
#version 420 core
uniform vec2 p0=vec2(0.0,0.0);      // mouse position <-1,+1>
uniform float zoom=1.000;           // zoom [-]
uniform int  n=100;                 // iterations [-]
uniform int  sh=7;                  // fixed point accuracy [bits]
uniform int  multipass=0;           // multi pass?
in smooth vec2 p;
out vec4 col;

const int n0=1;                     // forced iterations after escape to improve precision

vec3 spectral_color(float l)        // RGB <0,1> <- lambda l <400,700> [nm]
    {
    float t;  vec3 c=vec3(0.0,0.0,0.0);
         if ((l>=400.0)&&(l<410.0)) { t=(l-400.0)/(410.0-400.0); c.r=    +(0.33*t)-(0.20*t*t); }
    else if ((l>=410.0)&&(l<475.0)) { t=(l-410.0)/(475.0-410.0); c.r=0.14         -(0.13*t*t); }
    else if ((l>=545.0)&&(l<595.0)) { t=(l-545.0)/(595.0-545.0); c.r=    +(1.98*t)-(     t*t); }
    else if ((l>=595.0)&&(l<650.0)) { t=(l-595.0)/(650.0-595.0); c.r=0.98+(0.06*t)-(0.40*t*t); }
    else if ((l>=650.0)&&(l<700.0)) { t=(l-650.0)/(700.0-650.0); c.r=0.65-(0.84*t)+(0.20*t*t); }
         if ((l>=415.0)&&(l<475.0)) { t=(l-415.0)/(475.0-415.0); c.g=             +(0.80*t*t); }
    else if ((l>=475.0)&&(l<590.0)) { t=(l-475.0)/(590.0-475.0); c.g=0.8 +(0.76*t)-(0.80*t*t); }
    else if ((l>=585.0)&&(l<639.0)) { t=(l-585.0)/(639.0-585.0); c.g=0.84-(0.84*t)           ; }
         if ((l>=400.0)&&(l<475.0)) { t=(l-400.0)/(475.0-400.0); c.b=    +(2.20*t)-(1.50*t*t); }
    else if ((l>=475.0)&&(l<560.0)) { t=(l-475.0)/(560.0-475.0); c.b=0.7 -(     t)+(0.30*t*t); }
    return c;
    }

void main()
    {
    int i,j,N;
    vec2 pp;
    float x,y,q,xx,yy,mu;
    pp=(p/zoom)-p0;         // y (-1.0, 1.0)
    pp.x-=0.5;              // x (-1.5, 0.5)
    for (x=0.0,y=0.0,xx=0.0,yy=0.0,i=0;(i<n-n0)&&(xx+yy<4.0);i++)
        {
        q=xx-yy+pp.x;
        y=(2.0*x*y)+pp.y;
        x=q;
        xx=x*x;
        yy=y*y;     
        }
    for (j=0;j<n0;j++,i++)  // 2 more iterations to diminish fraction escape error
        {
        q=xx-yy+pp.x;
        y=(2.0*x*y)+pp.y;
        x=q;
        xx=x*x;
        yy=y*y;
        }
    mu=float(i)-log(log(sqrt(xx+yy))/log(2.0));
    mu*=float(1<<sh); i=int(mu);
    N=n<<sh;
    if (i>N) i=N;
    if (i<0) i=0;

    if (multipass!=0)
        {
        // i
        float r,g,b;
        r= i     &255; r/=255.0;
        g=(i>> 8)&255; g/=255.0;
        b=(i>>16)&255; b/=255.0;
        col=vec4(r,g,b,255);
        }
    else{
        // RGB
        q=float(i)/float(N);
        q=pow(q,0.2);
        col=vec4(spectral_color(400.0+(300.0*q)),1.0);
        }
    }
Run Code Online (Sandbox Code Playgroud)

这是单程分数转义n=100*32

分数逃逸

这是单程整数转义n=100

整数转义

正如您所看到的,对于相同的迭代次数 ( 100) ,分数转义要好得多。

最后很好的多通道(作为炫耀)只有 256 次迭代和 ~300x 缩放:

多次通过

与单程相比:

单程

关于修改的一些说明:

我向sh计数器(定点)添加了小数位部分。所以最大计数现在是,n<<sh而不仅仅是n. 我还添加了n0常数,以降低转义小数部分的误差。该链接建议使用 2 次迭代,但我认为 1 次看起来更好(它还i+1从对数方程中删除了增量)。迭代循环不变,我只是n0在它之后添加相同的迭代,然后计算分数转义mu并将其转换为定点(因为我的着色器输出整数)。

多通道仅在 CPU 端代码上更改。它只是重新索引使用过的索引,因此它们中没有孔,并使用可见光谱颜色重新着色。

这里演示: