0.00
60.0 fps

Mandelbulb

Raymarching rendering of mandelbulb

Log in to post a comment.

#version 300 es
precision highp float;

const float ambient = 0.1;
const vec3 ambientCol = vec3(0,0,1);

const float specStrength = 0.3;
const vec3 specCol = vec3(1);
const float shininess = 4.0;

const float paleness = 0.3;

uniform int iterations; // value=16 min=1 max=32 step=1
uniform float power; // value=2.0 min=2.0 max=32.0 step=1.0

const float h = 0.001;
const vec3 bulbPos = vec3(0,0,5);


uniform float iTime;
uniform vec2  iResolution;

in vec2 vScreen;
out vec4 fragColor;

/**
 * Returns vec2(
 *      the actual value,
 *      the number of needed iterations
 * ).
*/
vec2 mandelbulb(vec3 q) {
    float a = iTime * 0.2;
    q /= 3.5;
    vec3 p = vec3(q.x, q.z, q.y);
    p = vec3(
        cos(a) * p.x - sin(a) * p.y,
        sin(a) * p.x + cos(a) * p.y,
        p.z
    );
    
    const float bailout = 2.0;

    vec3 z = p;
    float dr = 1.0;
    float r = 0.0;

    int i;
    for (i = 0; i < iterations; i++) {
        r = length(z);
        if (r > bailout) break;

        // Spherical coordinate transformation
        float theta = acos(z.z / r);
        float phi = atan(z.y, z.x);
        dr = pow(r, power - 1.0) * power * dr + 1.0;

        float zr = pow(r, power);
        theta *= power;
        phi *= power;

        // Convert back to Cartesian coordinates
        z = zr * vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)) + p;
    }

    return vec2(
        0.5 * log(r) * r / dr,
        float(i)
    );
}

float sdf(vec3 p) {
    return mandelbulb(p - bulbPos).x;
}

vec3 march(vec3 p, vec3 dir) {
    for (int i=0; i<200; i++) {
        float d = sdf(p);
        d = abs(d);
        p += d * dir;
        if (d < h || d > 1000.0) break;
    }
    return p;
}

vec3 gradient(vec3 p) {
    return vec3(
        sdf(p + vec3(h, 0, 0)) - sdf(p - vec3(h, 0, 0)),
        sdf(p + vec3(0, h, 0)) - sdf(p - vec3(0, h, 0)),
        sdf(p + vec3(0, 0, h)) - sdf(p - vec3(0, 0, h))
    ) / (2.0 * h);
}

vec3 rainbow(float r) {
    r *= 6.28318530718;
    return vec3(
        0.5 * sin(r + 0.000 - 0.0 * iTime) + 0.5, 
        0.5 * sin(r + 2.094 - 0.0 * iTime) + 0.5, 
        0.5 * sin(r + 4.188 - 0.0 * iTime) + 0.5
    );
}

vec3 rainbowLowRes(float r, int n) {
    r = floor(float(n) * r) / float(n);
    return rainbow(r);
}

void main() {
    
    vec3 o = vec3(0, 0, -1);
    vec3 dir = normalize(vec3(vScreen, 0) - o);
    o += vec3(0, 0, 0);
    
    vec3 p = o;
    
    p = march(p, dir);
    
    bool collision = sdf(p) < h;
    if (collision) {
        float c = dot(
            normalize(p - o),
            normalize(gradient(p))
        );
        c = abs(c);
        c -= 0.5;
        c -= floor(c);
        
        vec3 lightPos = o + vec3(2, 4, 0);
        vec3 N = normalize(gradient(p));
        vec3 L = normalize(lightPos - p);
        float diff = max(0.0, dot(N, L));
        vec3 R = reflect(-L, N);
        float spec = specStrength * pow(max(0.0, dot(-dir, R)), shininess);
        
        float numIterations = mandelbulb(p - bulbPos).y;
        vec3 diffCol = rainbow(numIterations / float(iterations));
        diffCol = mix(diffCol, vec3(1), paleness);  // make it a bit more pale
        
        vec3 col = ambient * ambientCol
                 + diffCol * diff
                 + spec * specCol;
        
        fragColor = vec4(col, 1);
        
        
        
    } else {
        fragColor = vec4(0, 0, 0, 1);
    }
}