java math processing fractals mandelbrot

java - No puedo encontrar la manera de colorear Mandelbrot: establezca la forma en la que estoy apuntando



math processing (1)

He tenido éxito en colorear el conjunto de Mandelbrot, aunque no puedo acercar mucho el zoom hasta que se vuelve "borroso" y el patrón se detiene. Arreglo esto al aumentar el max_iteration, esto funciona pero tengo muy pocos colores con * 1 ampliación y muchos de los colores solo aparecen cuando me acerco. Entiendo por qué sucede esto ya que en un "verdadero" conjunto de Mandelbrot no hay colores y aumentar las max_iterations solo lo acerca a eso. Pero mi pregunta es, ¿cómo los zooms, como en YouTube, tienen hermosos colores durante todo el proceso de zoom y aún así se pueden ampliar para lo que se siente para siempre?

He intentado buscar en todas partes en línea, pero no puedo encontrar una solución y cuando miro las descripciones de estos zooms de youtube, parecen proporcionar casi nada sobre cómo hicieron ese zoom.

Aquí está solo la sección de código que dibuja el conjunto de Mandelbrot. El siguiente código está escrito en el procesamiento que es java con bibliotecas visuales agregadas. Puede encontrar más información sobre el programa aquí: 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);

Entiendo por qué está sucediendo mi problema pero no sé cómo resolverlo.

Aquí hay una imagen con un máximo de max_iteraciones y alejada, como se puede ver, es muy colorida:

Aquí hay una imagen con un máximo de max_iteraciones y un poco de zoom, como puedes ver, es aburrida y no muy colorida:

Aquí hay una imagen con un máximo de max_iteraciones y alejada, como puede ver, no es muy colorida:

Aquí hay una imagen con un máximo de max_iteraciones y zoom, como se puede ver, es muy colorido:


Primero eche un vistazo a este QA relacionado:

  • Mandelbrot Set - Color Spectrum Suggestions?

La idea principal es usar el histograma para distribuir los gradientes de color de manera más efectiva a los índices utilizados en lugar de gastar uniformemente muchos colores en los índices no utilizados. También utiliza una función de gradiente visualmente agradable específica:

  • Valores RGB del espectro visible

El número máximo de iteraciones dinámicas sugerido por otros solo afectará el rendimiento general y los detalles en los zoom. Sin embargo, si desea colores agradables sin zoom , debe calcular el número de iteraciones de punto flotante, que también se denomina Mandelbrot Escape . Hay una forma matemática que puede calcular la parte fraccional del recuento de iteraciones a partir de los últimos sub-resultados de la ecuación. Para más información ver:

Sin embargo, nunca lo intenté, así que lea esto con prejuicio: si lo leo bien, lo que quiere es calcular esta ecuación:

mu = m + frac = n + 1 - log (log |Z(n)|) / log 2

Donde n es su recuento de iteraciones, Z(n) es el sub-resultado del dominio complejo de la ecuación en la que está iterando. Entonces ahora calcule el color de mu que ahora es un punto flotante en lugar de n ...

[Edit2] GLSL mandelbrot con escape fraccional basado en los enlaces anteriores

Agregué el escape fraccional y modifiqué el recolorado de múltiples pases del histograma para coincidir con la nueva salida ...

Vértice:

// 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); }

Fragmento:

// 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); } }

Código del lado C ++ / VCL de la CPU:

//--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "Unit1.h" #include "gl//OpenGL3D_double.cpp" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; OpenGLscreen scr; GLSLprogram shd; float mx=0.0,my=0.0,mx0=0.0,my0=0.0,mx1=0.0,my1=0.0; TShiftState sh0,sh1; int xs=1,ys=1; float zoom=1.000; int sh=7; int N=256; int _multi=0; unsigned int queryID[2]; #define multi_pass OpenGLtexture txr; //--------------------------------------------------------------------------- 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; } //--------------------------------------------------------------------------- void gl_draw() { scr.cls(); // matrix for old GL rendering glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glMatrixMode(GL_TEXTURE); glLoadIdentity(); // GLSL uniforms shd.bind(); shd.set2f("p0",mx,my); // pan position shd.set1f("zoom",zoom); // zoom shd.set1i("n",N); // iterations shd.set1i("sh",sh); // fixed point accuracy (shift) shd.set1i("multipass",_multi); // single/multi pass // issue the first query // Records the time only after all previous // commands have been completed glQueryCounter(queryID[0], GL_TIMESTAMP); // QUAD covering screen glColor3f(1.0,1.0,1.0); glBegin(GL_QUADS); glVertex2f(-1.0,+1.0); glVertex2f(-1.0,-1.0); glVertex2f(+1.0,-1.0); glVertex2f(+1.0,+1.0); glEnd(); shd.unbind(); // [multipas] if (_multi) { float t,m,n=N<<sh; DWORD *hist=new DWORD[n+1]; int sz=txr.xs*txr.ys,i,j; // 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]++; // histogram -> used color index (skip holes) for (i=1,j=1;i<=n;i++) if (hist[i]){ hist[i]=j; j++; } // used color index -> color m=1.0/float(j); hist[0]=0x00000000; for (i=1;i<=n;i++) if (hist[i]){ t=hist[i]; t*=m; hist[i]=spectral_color(400.0+(300.0*t)); } else hist[i]=0x00000000; // recolor image for (i=0;i<sz;i++) txr.txr[i]=hist[txr.txr[i]&0x00FFFFFF]; // 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); delete[] hist; } // issue the second query // records the time when the sequence of OpenGL // commands has been fully executed glQueryCounter(queryID[1], GL_TIMESTAMP); // GL driver info and GLSL log scr.text_init_pix(0.75); glColor4f(1.0,1.0,1.0,0.9); scr.text(glGetAnsiString(GL_VENDOR)); scr.text(glGetAnsiString(GL_RENDERER)); scr.text("OpenGL ver: "+glGetAnsiString(GL_VERSION)); if (_multi) scr.text("Multi pass"); else scr.text("Single pass"); if (shd.log.Length()!=41) for (int i=1;i<=shd.log.Length();) scr.text(str_load_lin(shd.log,i,true)); scr.text_exit(); scr.exe(); scr.rfs(); // wait until the results are available int e; unsigned __int64 t0,t1; for (e=0;!e;) glGetQueryObjectiv(queryID[0],GL_QUERY_RESULT_AVAILABLE,&e); for (e=0;!e;) glGetQueryObjectiv(queryID[1],GL_QUERY_RESULT_AVAILABLE,&e); glGetQueryObjectui64v(queryID[0], GL_QUERY_RESULT, &t0); glGetQueryObjectui64v(queryID[1], GL_QUERY_RESULT, &t1); Form1->Caption=AnsiString().sprintf("dt: %f ms p0:%.3fx%.3f zoom: %.1lf N:%i<<%i/n",(t1-t0)/1000000.0,mx,my,zoom,N,sh); } //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner) { scr.init(this); shd.set_source_file("","","","Mandelbrot_set.glsl_vert","Mandelbrot_set.glsl_frag"); glGenQueries(2, queryID); // nice spirals _multi=1; zoom=300.0; mx = 0.268; my =-0.102; } //--------------------------------------------------------------------------- void __fastcall TForm1::FormDestroy(TObject *Sender) { scr.exit(); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormResize(TObject *Sender) { scr.resize(); xs=ClientWidth; ys=ClientHeight; txr.resize(xs,ys); gl_draw(); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormPaint(TObject *Sender) { gl_draw(); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X,int Y) { bool q0,q1; mx1=1.0-divide(X+X,xs-1); my1=divide(Y+Y,ys-1)-1.0; sh1=Shift; q0=sh0.Contains(ssLeft); q1=sh1.Contains(ssLeft); if (q1) { mx-=(mx1-mx0)/zoom; my-=(my1-my0)/zoom; } mx0=mx1; my0=my1; sh0=sh1; gl_draw(); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { FormMouseMove(Sender,Shift,X,Y); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { FormMouseMove(Sender,Shift,X,Y); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormMouseWheel(TObject *Sender, TShiftState Shift, int WheelDelta, TPoint &MousePos, bool &Handled) { if (WheelDelta>0) zoom*=1.2; if (WheelDelta<0) zoom/=1.2; Handled=true; gl_draw(); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { Caption=Key; if (Key==32){ _multi=!_multi; gl_draw(); } // [Space] if (Key==33){ if (N<8192) N<<=1; gl_draw(); } // [PgUp] if (Key==34){ if (N> 128) N>>=1; gl_draw(); } // [PgDown] } //---------------------------------------------------------------------------

Esto es escape fraccional de una sola pasada n=100*32 :

Este es un paso entero de escape n=100 :

Como puede ver, el escape fraccional es mucho mejor para el mismo número de iteraciones ( 100 ).

Y finalmente, un pase múltiple agradable (como un showoff) solo 256 iteraciones y un zoom de ~ 300x:

contra una sola pasada:

Algunas explicaciones sobre la modificación:

sh fraccionaria de sh al contador (punto fijo). Así que el recuento máximo es ahora n<<sh lugar de solo n . También agregué n0 constante que reduce el error de la parte fraccionaria de escape. El enlace sugiere usar 2 iteraciones pero 1 parece mejor (creo que también elimina el incremento de i+1 de la ecuación logarítmica). El bucle de iteración no se modifica. Solo agrego n0 iteraciones después de él y luego calculo el escape fraccionario mu y lo convierto a un punto fijo (ya que mi sombreador da como resultado un entero).

El paso múltiple se cambia solo en el código del lado de la CPU. Simplemente vuelve a indexar los índices utilizados para que no haya agujeros en ellos y vuelva a colorear usando colores de espectros visibles.

Aquí Demo: