Набор Мандельброта - предложения цветового спектра? - PullRequest
0 голосов
/ 06 декабря 2018

Недавно я начал делать набор Мандельброта, но не смог найти хорошую цветовую гамму.Вот мой результат

Mandelbrot set:

Но я хочу создать что-то вроде этого: enter image description here

Любые предложения?Спасибо!

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018
  1. попробуйте использовать вычисленную палитру вместо текстуры

    Например, это мое:

  2. для более высокой детализации требуется большее число итераций

    так сколько у вас итераций?Если я применяю # 1 и использую n=200 итераций это:

    Я получил это:

    n = 200 linear

    Что более или менее напоминает ваш случай (кроме соотношения сторон, но все же не уверен, что у нас одинаковый набор ...).Количество итераций влияет не только на количество деталей, но и на общую яркость результата, поэтому будьте осторожны, не используйте слишком много или слишком мало.

  3. zoom

    Как только вы начнете масштабировать, детали начнут появляться гораздо глубже ... Это n=2000 и увеличит третью копию справа ...

    n=2000 linear zoomed

    Как вы можете видеть, есть много деталей, которые не видны на предыдущем изображении

  4. дальнейшее улучшение

    Вы можете использовать нелинейные масштабы для отображения от итерации к цвету.Итак, снова тот же масштаб, но с использованием t=pow(t,0.5); в качестве параметра для цвета ...

    n=2000 non linear zoomed

    Как вы можете видеть, результат выглядит лучше (более плавный)) ...

    n=250 non linear zooming

    Последнее, что я могу придумать, это реализовать сортировку HDR ... поэтому вычислите первое базовое изображение с небольшим n затем осмотрите его и найдите зоны с низким уровнем детализации (практически без изменения цвета) и выполните повторную визуализацию с более высокими значениями n до тех пор, пока у вас не будет таких зон ... Для каждого n вы можете немного настроить функцию разграничения параметров...

    Также вы можете найти параметр min и max для всего результата и вычислить цвет только из этого диапазона, охватывающего весь спектр из этого ... как:

    l = 400 + 300*(i-imin)/(imax-imin)
    

    где l - длина волны в [nm] для цветового спектра, i - конечная итерация, а imin,imax - минимальное и максимальное значения для всего изображения ... к сожалению, я не могу попробовать это легко, так как мне нужно будет переписатьGLSL рендерит в два прохода рендерера.

    Еще одна вещь, которую выможно использовать гистограмму вместо min max ... но это также многоходовая техника ...

[Edit1] match

Также, сравнивая с чужими изображениями, убедитесь, что они используют один и тот же набор ... в противном случае вы сравниваете апельсины с яблоками.Когда я посмотрел поближе, это стало очевидным, я получил тот же набор, но с другой шкалой на х ... и изображение увеличилось в определенной точке фрактала.После некоторой настройки наиболее близкое совпадение, которое я нашел, было таким:

zoomed match

[Edit2] гистограмма

После изменениямой код для поддержки многопроходного рендеринга я мог бы применить подход гистограммы ... Так что

  1. рендер Мандельброта установлен на текстуру

    , но вместо цветов рендерингиндекс итерации ...

  2. вычисление гистограммы отрисованного материала

  3. поиск диапазона индексов для перекраски

    гистограмма будет иметь много записей с нулевым или малым количеством пикселей ... и затем будет группа более высоких значений (то есть диапазон, который вы ищете), и после этого будет небольшое илиснова нулевые значения ... Вдобавок ко всему этому последнее значение может содержать большое количество пикселей (то есть черная пустая область), и может быть одно или несколько более высоких значений, слишком независимых от основной группы.Если я использую 80% спектральных цветов для основной группы / диапазона и 10% для индексов до него и 10% для индексов после него, результат будет выглядеть следующим образом:

    histogram

Здесь модифицированный код GLSL с восстановленным масштабом, масштабированием и переключением между однопроходным / многопроходным рендерингом:

// 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);
    }
// Fragment
#version 420 core
#define multi_pass
uniform vec2 p0=vec2(0.0,0.0);      // mouse position <-1,+1>
uniform float zoom=1.000;           // zoom [-]
uniform int  n=4000;                // iterations [-]
in smooth vec2 p;
out vec4 col;
#ifdef multi_pass
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;
    }
#endif
void main()
    {
    int i,j;
    vec2 pp;
    float x,y,q,xx,yy;
    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)&&(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;
        }
    #ifndef multi_pass
    // RGB
    q=float(i)/float(n);
    q=pow(q,0.2);
    col=vec4(spectral_color(400.0+(300.0*q)),1.0);
    #else
    // 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);
    #endif
    }

Изображениеберется за

n=4095;            // max iterations
zoom=1763.0;       // zoom [-]
p0.x=0.1483064;    // center position
p0.y=0.3742866;

И второй проход рендеринга выглядит так:

// globals
const int N=4095; // this is the max count of iterations
OpenGLtexture txr;

// helper functions
DWORD spectral_color(float l)        // RGB <0,1> <- lambda l <400,700> [nm]
    {
    float t;  float r,g,b; DWORD c,x; r=0.0; g=0.0; b=0.0;
         if ((l>=400.0)&&(l<410.0)) { t=(l-400.0)/(410.0-400.0); r=    +(0.33*t)-(0.20*t*t); }
    else if ((l>=410.0)&&(l<475.0)) { t=(l-410.0)/(475.0-410.0); r=0.14         -(0.13*t*t); }
    else if ((l>=545.0)&&(l<595.0)) { t=(l-545.0)/(595.0-545.0); r=    +(1.98*t)-(     t*t); }
    else if ((l>=595.0)&&(l<650.0)) { t=(l-595.0)/(650.0-595.0); 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); 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); g=             +(0.80*t*t); }
    else if ((l>=475.0)&&(l<590.0)) { t=(l-475.0)/(590.0-475.0); 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); g=0.84-(0.84*t)           ; }
         if ((l>=400.0)&&(l<475.0)) { t=(l-400.0)/(475.0-400.0); b=    +(2.20*t)-(1.50*t*t); }
    else if ((l>=475.0)&&(l<560.0)) { t=(l-475.0)/(560.0-475.0); b=0.7 -(     t)+(0.30*t*t); }
    r*=255.0; g*=255.0; b*=255.0;
    x=r; c =x;
    x=g; c|=x<<8;
    x=b; c|=x<<16;
    return c;
    }

    ...
    // [multipass] this is executed after the shader renders its stuff
    int hist[N+1],sz=txr.xs*txr.ys,i,i0,i1,a0,a1;
    float t;
    // get rendered image
    glReadPixels(0,0,txr.xs,txr.ys,GL_RGBA,GL_UNSIGNED_BYTE,txr.txr);
    // compute histogram
    for (i=0;i<N;i++) hist[i]=0;
    for (i=0;i<sz;i++) hist[txr.txr[i]&0x00FFFFFF]++;
    // find the major used range
    a0=txr.xs/4;
    a1=txr.xs*4;
    for (i0=  0;(i0<N)&&((hist[i0]<a0)||(hist[i0]>a1));i0++);
    for (i1=N-1;(i1>0)&&((hist[i1]<a0)||(hist[i1]>a1));i1--);
    // recolor it
    for (i=0;i<sz;i++)
        {
        a0=txr.txr[i]&0x00FFFFFF;
             if (a0<i0) t=(0.1*divide(a0   ,i0   ));
        else if (a0>i1) t=(0.1*divide(a0-i1,N -i1))+0.9;
        else            t=(0.8*divide(a0-i0,i1-i0))+0.1;
        txr.txr[i]=spectral_color(400.0+(300.0*t));
        }
    // render it back
    scr.cls();
    txr.bind();
    glColor3f(1.0,1.0,1.0);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0,1.0); glVertex2f(-1.0,+1.0);
    glTexCoord2f(0.0,0.0); glVertex2f(-1.0,-1.0);
    glTexCoord2f(1.0,0.0); glVertex2f(+1.0,-1.0);
    glTexCoord2f(1.0,1.0); glVertex2f(+1.0,+1.0);
    glEnd();
    txr.unbind();
    glDisable(GL_TEXTURE_2D);

Я знаю, что он использует много вещей, которыми я не поделился, но использование гистограммы достаточно просто, чтобы портировать его для ваших нужд ...

Так что теперь просто нужно найти правильную комбинацию n и позиции / масштабирования.

[Edit3] Однако, если даже этого недостаточно

Затем вы можете реализовать дробный побег для получения дополнительной информации см .:

0 голосов
/ 07 декабря 2018

При более глубоком увеличении контуры не только намного ближе друг к другу, чем на базовом изображении («пейзаж» поднимается более круто), но и используемая палитра имеет меньшие переходы.Палитра, используемая в базовом изображении, имеет большой шаг в значениях цвета между смежными контурами, делая только 9 шагов для перехода от темно-синего к желтому.

Если вы используете один шаг значения только в одном из трех основных цветовот одного контура к другому вы все еще можете различить разницу, но она менее очевидна и выглядит гораздо более плавной.Я бы сделал это с помощью предварительно рассчитанной справочной таблицы.

...