0.00
60.0 fps

Mandelbrot and Julia set

Simple version of the Mandelbrot and Julia set. The surrender function can contain any shader you like. It takes an x and y between 0 and 1. Next step perturbation theory

Log in to post a comment.

#version 300 es
precision highp float;

uniform float iTime;
uniform vec2  iResolution;

uniform float ShadeMode; // value=1, min=0, max=1, step=1 (No, Yes)
uniform float ImageMode; // value=1, min=0, max=1, step=1 (No, Yes)
uniform float ImageXScale; // value=1, min=0, max=2, step=0.1
uniform float ImageYScale; // value=1, min=0, max=2, step=0.1
uniform float Front; // value=1, min=0, max=1, step=1 (No, Yes)
uniform float Animate; // value=1, min=0, max=1, step=1 (No, Yes)
uniform float Radius; // value=0.5, min=0, max=1, step=0.01
uniform float Normalised; // value=1, min=0, max=1, step=1 (No, Yes)
uniform float Julia; // value=1, min=0, max=1, step=1 (No, Yes)
uniform float Julia_c1; // value=0, min=-1.0, max=1.0, step=0.01
uniform float Julia_c2; // value=0, min=-1.0, max=1.0, step=0.01
uniform int MaxIter; // value=50, min=1, max=1000, step=1
uniform int AA; // value=4, min=1, max=9, step=1
uniform float Zoom; // value=1, min=0.01, max=9, step=0.01
uniform float Xoffset; // value=0, min=-1, max=1, step=0.01
uniform float Xpan; // value=0, min=-1, max=1, step=0.01
uniform float Yoffset; // value=0, min=-1, max=1, step=0.01
uniform float Ypan; // value=0, min=-1, max=1, step=0.01
uniform float light; // value=1.5, min=0, max=4, step=0.01
uniform float angle; // value=45, min=0, max=180, step=0.1
uniform float EscapeRadius; // value=4, min=0, max=100, step=0.01

float jc1;
float jc2;

out vec4 fragColor;

vec2 cMul(vec2 a, vec2 b) {
    return vec2( a.x*b.x -  a.y*b.y,a.x*b.y + a.y * b.x);
}

vec2 cInverse(vec2 a) {
    return  vec2(a.x,-a.y)/dot(a,a);
}
vec2 cDiv(vec2 a, vec2 b) {
    return cMul( a,cInverse(b));
}
vec2 cExp(in vec2 z){
    return vec2(exp(z.x)*cos(z.y),exp(z.x)*sin(z.y));
}

// https://thebookofshaders.com/10/
// Random function
float random (vec2 st) {
    return fract(sin(dot(st.xy,
                         vec2(12.9898,78.233)))*
        43758.5453123);
}

// Colour scheme
// Stolen from llemarie
// vec3 HSVtoRGB(float iter){
//     iter = mod(iter+iTime, float(MaxIter));
//     float value = 1. - float(iter)/float(MaxIter);
//     value *= value;
        
//     float h = value;
//     float s = 1.;
//     float v = (iter < float(MaxIter)) ? 1. - value*value : 0.;
    
//     float r, g, b, f, p, q, t;
//     int i;
//     i = int(h * 6.);
//     f = h * 6. - float(i);
//     p = v * (1. - s);
//     q = v * (1. - f * s);
//     t = v * (1. - (1. - f) * s);
//     switch (i % 6){
//         case 0: r = v, g = t, b = p; break;
//         case 1: r = q, g = v, b = p; break;
//         case 2: r = p, g = v, b = t; break;
//         case 3: r = p, g = q, b = v; break;
//         case 4: r = t, g = p, b = v; break;
//         case 5: r = v, g = p, b = q; break;
//     }
//     return vec3(r, g, b);
// }

vec3 palette(float t)
{
    //return vec3(0.5,0.5,0.5) + vec3(0.5,0.5,0.5)*cos(6.28318*(vec3(2.0,1.0,0.0)*t + vec3(0.5,0.2,0.25)));
    return vec3(0.5) + vec3(0.5)*cos(6.28318*(vec3(1.0)*t + vec3(0.,0.33,0.67) + iTime));
    //return vec3(0.5, 0.5, 0.5) + vec3(0.5, 0.5, 0.5)*cos(6.28318*(vec3(1.0, 1.0, 1.0)*t+2.*iTime*vec3(0.01, 0.10, 0.20)) );
}


// Put anything you like here. This is the image that is rendered in the image orbit traps
vec4 subrender(float xin, float yin){
    xin = xin/ImageXScale;
    yin = yin/ImageYScale;
    float sqr = sqrt((xin-0.5)*(xin-0.5)+(yin-0.5)*(yin-0.5));
    if(sqr<Radius){
        return vec4(sin(xin + iTime)*2. + 0.5,yin,sqr,1.0);
    }
    else{
        return vec4(0.0);
    }
}

vec4 render(float xin, float yin){
    vec4 final_col = vec4(0.0);
    for(int i = 0; i<AA;i++){
        float x = 0.0;
        float y = 0.0;
        float xx = 0.0;
        float yy = 0.0;
        float xy = 0.0;
        
        // sample points around the main pixel for anti-aliasing
        float r = random(vec2(xin + float(i),yin + float(i)))*0.0001;
        float c1 = (1./Zoom)*(xin + Xoffset + r + Xpan);
        float c2 = (1./Zoom)*(yin + Yoffset + r - Ypan);
        
        // Julia mode
        if(Julia>0.0){
            x = c1;
            y = c2;
            c1 = jc1;
            c2 = jc2;
        }
        
        vec2 dc = vec2(1, 0);
        vec2 der = dc;
        
        float h2 = light;
        float angle = angle;
        vec2 v = cExp(vec2(0.0, 45.*2.*3.1415/360.));
        v = vec2(sin(iTime),cos(iTime));
        
        int j = 0;
        // Flag keeps track of opqque pixels
        int flag = 0;
        
        vec4 col = vec4(palette(1.0), 1.0);
        
        while(j<MaxIter){
            xx = x*x;
            yy = y*y;
            if((xx + yy) > EscapeRadius){
                if(flag<1){
                    // Normalised/Smooth iteration count
                    if(Normalised>0.0){
                        float iter =float(j) - log2(log2(dot(vec2(x,y),vec2(x,y)))) + 4.0;
                        
                        // float logzn = log(xx+yy);
                        // float log4 = log(4.0);
                        // float log2 = 1.44269504089;
                        // float logzndiv4 = logzn/log4;
                        // float loglogzndiv4 = log(logzndiv4);
                        // float nu = loglogzndiv4 * 1.0/log2;
                        // nu = log(logzn/log(4.0))/log(2.0);
                        // float iter = float(j) + 1.0 - nu;
                        
                        col = vec4(palette(iter), 1.0);
                    }
                    else{
    
                        
                        col = vec4(palette(float(j)), 1.0);
                    }
                }
                
                break;
            }
            der = cMul(vec2(x,y), der) * 2.0 + dc;
            
            // TODO: Karatsuba style - not sure how much time it saves. https://mathr.co.uk/blog/2016-04-10_complex_squaring.html
            xy = x*y;
            x = xx - yy + c1;
            y = xy+xy + c2;
            
            // Image orbit trap
            if(ImageMode>0.0 && x>-1.0*ImageXScale && x<=1.0*ImageXScale && y > -1.0*ImageYScale && y<=1.0*ImageYScale){
                vec4 sr = subrender(x+0.5, y+0.5);
                if(sr.a>0.0){
                    col = sr;
                    if(Front>0.0){
                        break;
                    }
                    flag = 1;
                }
            }
            j++;
        }
        
        if(ShadeMode>0.0){
        
            vec2 u = cDiv(vec2(x,y), der);
            u = cDiv(u, abs(vec2(u.x, u.y)));
            float t = dot(u,v) + h2;
            t = t/(1. + h2);
            
            if(t<0.){
                t =0.;
            }
            
            col.xyz *= t;
        }
        
        final_col += col;
        
        

    }
    
    return final_col/float(AA);
}

void main() {
    // Screen coordinate (from [-aspect, -1] to [aspect, 1])
    vec2 q   = (2. * gl_FragCoord.xy - iResolution) / iResolution.y;
    
    
    jc1 = Julia_c1;
    jc2 = Julia_c2;
    if(Animate>0.0){
        jc1 = sin(Julia_c1 + iTime + 5.);
        jc2 = cos(Julia_c2 + iTime + 7.);
    }
    
    vec4 col = render(q.x, q.y);
    col.xyz = pow(col.xyz, vec3(0.9));
    
    // Output
    fragColor = col;
}