c++ - 我如何获得无限小的数字(对于分形)

标签 c++ opengl glsl fractals mandelbrot

我正在使用OpenGL使用C++对Mandelbrotset进行编程,但是我遇到了一个问题:我要发送给着色器并在其中进行计算的浮点数只能容纳一定数量的小数位。因此,如果我放大得太远,它只会变得像素化。
我曾考虑过创建自定义数组函数,但我真的无法弄清楚。
除了使用数组还有其他方法吗?如果不是,我怎么能像数组一样使用数组来计算东西呢? (例如arr [1,2] x arr [0,2]应提供与仅计算1.2 x 0.2相同的输出)

in vec4 gl_FragCoord;
 
out vec4 frag_color;
uniform float zoom;
uniform float x;
uniform float y;
#define maximum_iterations 1000

int mandelbrot_iterations()
{
    float real = ((gl_FragCoord.x / 1440.0f - 0.5f) * zoom + x) * 4.0f;
    float imag = ((gl_FragCoord.y / 1440.0f - 0.5f) * zoom + y) * 4.0f;
 
    int iterations = 0;
    float const_real = real;
    float const_imag = imag;
 
    
    while (iterations < maximum_iterations)
    {
        float tmp_real = real;
        real = (real * real - imag * imag) + const_real;
        imag = (2.0f * tmp_real * imag) + const_imag;
         
        float dist = real * real + imag * imag;
 
        if (dist > 4.0f)
            break;
 
        ++iterations;
    }
    return iterations;
}
^我的fragmenthader中的Mandelbrot函数

最佳答案

正如其他人建议的那样,请使用double而不是float,这将为您提供更高的缩放比例。最重要的是,使用分数转义可以允许更多细节,而迭代次数更少,因此可以更快地获取细节,请参见我的文章:

  • GLSL Mandelbrot

  • 里面有我的Mandelbrot的 float 代码,并链接到32位和64位 float 的Win32演示。但是double版本着色器不适合回答,因此这里是(对于分形,第二遍重新着色着色器并不重要,但是可以从演示中提取它们):
    // Fragment
    #version 450 core
    uniform dvec2 p0=vec2(0.0,0.0);     // mouse position <-1,+1>
    uniform double zoom=1.000;          // zoom [-]
    uniform int  n=100;                 // iterations [-]
    uniform int  sh=7;                  // fixed point accuracy [bits]
    uniform int  multipass=0;           // multi pass?
    uniform int  inverted=0;            // inverted/reciprocal position?
    in smooth vec2 p32;
    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;
        dvec2 pp,p;
        double x,y,q,xx,yy,mu,cx,cy;
        p=dvec2(p32);
    
        pp=(p/zoom)-p0;         // y (-1.0, 1.0)
        pp.x-=0.5;              // x (-1.5, 0.5)
        if (inverted!=0)
            {
            cx=pp.x/((pp.x*pp.x)+(pp.y*pp.y));  // inverted
            cy=pp.y/((pp.x*pp.x)+(pp.y*pp.y));
            }
        else{
            cx=pp.x;                // normal
            cy=pp.y;
            }
        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+cx;
            y=(2.0*x*y)+cy;
            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+cx;
            y=(2.0*x*y)+cy;
            x=q;
            xx=x*x;
            yy=y*y;
            }
        mu=double(i)-double(log2(log(float(sqrt(xx+yy)))));
        mu*=double(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
            float q=float(i)/float(N);
            q=pow(q,0.2);
            col=vec4(spectral_color(400.0+(300.0*q)),1.0);
            }
        }
    
    和:
    // Vertex
    #version 450 core
    layout(location=0) in vec2 pos;         // glVertex2f <-1,+1>
    out smooth vec2 p32;                    // texture end point <0,1>
    void main()
        {
        p32=pos;
        gl_Position=vec4(pos,0.0,1.0);
        }
    
    这可以上升到开始出现像素化的zoom = 1e+14:
    pixelation
    在GPU上使用任意精度的float将非常缓慢且存在问题(正如其他人已经建议的那样)。但是,有一些更简单的解决方法可以提高float或double的精度。
    例如,您可以将值保留为更多doubles的总和...
    代替
    double x;
    
    您可以使用:
    double x0,x1,x2,....,xn;
    
    其中x = x0+x1+x2+...+xn的位置x0的值较小,x1的值较大,... xn的值最大。您只需要基本的+,-,*操作,因此
  • x += y
    x0+=y0; x1+=y1; ... xn+=yn;
    
  • x -= y
    x0-=y0; x1-=y1; ... xn-=yn;
    
  • x *= y
    x0*=(y0+y1+...yn);
    x1*=(y0+y1+...yn);
    ...
    xn*=(y0+y1+...yn);
    

  • 在执行每个操作后,您可以将每个变量的范围归一化:
       if (fabs(x0)>1e-10){ x1+=x0; x0=0; }
       if (fabs(x1)>1e-9) { x2+=x1; x1=0; }
       ...
       if (fabs(x(n-1))>1e+9){ xn+=x(n-1); x(n-1)=0; }
    
    您需要选择范围,这样您就不会在不会使用的数字上浪费变量...
    精度仍然受到限制,但精度损失要小得多。
    但是仍然存在一个限制(这是不容易克服的)。现在,您正在从片段坐标,zoom和pan x,y计算位置,这些位置将被限制为float,因为我们仍然没有64位双插值器。如果要打破此障碍,则需要以与其余计算相同的方式(在更多变量但这次 float )来计算CPU侧或顶点上的缩放位置,并将结果作为片段传递

    关于c++ - 我如何获得无限小的数字(对于分形),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65010504/

    相关文章:

    c++ - 已弃用的 std::auto_ptr 的 c++11 标准等价物是什么?

    c++ - 使用 Opengl 绘制超过 50k 球体时如何提高速度

    c++ - 在 OpenGL 中旋转立方体

    c++ - glulookat() - 用键盘移动相机(OpenGL)

    opengl - OpenGL/GLSL 着色器中的统一位置之间是否存在间隙是否重要?

    c++ - 歧义语句 C++-AMP : (extent) is ambiguous

    c++ - Opencv 标志不起作用

    opengl-es - 具有可选属性的 GLSL 着色器

    glsl - 像素着色器中可以实现哪种模糊?

    c++ - 未定义对 Armadillo 的 LAPACK 包装器的引用