My atempt to get a mandelbrot rendering properly using only shaders, not quite there.

Lot’s of webgl boilerplate underneath

Updates

  • 2023-11-25: exprimented with open aliasing

precision highp float;

const float MAX_ITER = 4096.0;
uniform float iTime;
uniform vec2 iResolution;

float mandelbrot(vec2 uv) {
    vec2 c = 4.0 * uv - vec2(0.7, 0.0);
    c = c / pow(iTime * 0.2, 4.0) - vec2(0.65, 0.45);
    vec2 z = vec2(0.0);
    float iter = 0.0;
    float m;

    for(float i = 0.0; i < MAX_ITER; i++){
        z = vec2(z.x*z.x - z.y*z.y, 2.0*z.x*z.y) + c;
        if(dot(z,z) > 4.0) {
            // Compute smoothed iteration count
            // m = iter - log(log(length(z))) / log(2.0);
            m = iter;
            // return m/MAX_ITER;
            return iter;
        }
        iter++;
    }

    return MAX_ITER;
}

vec3 hash13(float m){
    float x = fract(sin(m) * 5624.246);
    float y = fract(sin(m + x) * 2216.486);
    float z = fract(sin(x + y) * 8276.352);

    return vec3(x, y, z);
}

vec3 color(float iter) {
    float r = 0.5 + 0.5*cos(3.0 + iter*0.15);
    float g = 0.5 + 0.5*cos(3.0 + iter*0.15 + 2.0);
    float b = 0.5 + 0.5*cos(3.0 + iter*0.15 + 4.0);
    return vec3(r, g, b);
}

void main() {
    vec2 uv = (gl_FragCoord.xy - 0.5 * iResolution.xy) / iResolution.y;
    vec3 col = vec3(0.0);
    const int samples = 4; // Increase for better quality
    for(int x = 0; x < samples; x++) {
        for(int y = 0; y < samples; y++) {
            vec2 sampleUv = uv + vec2(x, y) * (1.0 / iResolution.y) / float(samples);
            float m = mandelbrot(sampleUv);
            col += color(m) * hash13(m);
        }
    }
    col /= float(samples * samples);
    gl_FragColor = vec4(col, 1.0);
}
// Vertex shader
attribute vec2 a_position;

void main() {
    gl_Position = vec4(a_position, 0, 1);
}