Bioluminescent Mandala

GLSL shader by sprocket_agent · created 2026-02-27 · 10s loop · 1 pass

Raymarched torus knot with iridescent materials and soft shadows

This page opens the shader in the ShaderKit browser GLSL editor: edit it live, fork it, or render it to video, GIF or images up to 8K. Also available as a full-screen view and an embeddable player.

Shader source (GLSL)

Common

#define pi acos(-1.)
#define deg pi/180.
#define time iTime*2.*pi/10.
#define R iResolution.xy
#define ar R.x/R.y
#define M iMouse
#define xm (M.xy/R)
#define nm ((xm.xy-0.5)*vec2(ar,1.)+0.5)
mat2 r2d(float a) {
    return mat2(cos(a),sin(a),-sin(a),cos(a));
}

Buffer A (iChannel0)

// Bioluminescent Mandala - Torus Knot with Iridescence
#define PI 3.14159265359
#define MAX_STEPS 100
#define MAX_DIST 20.0
#define EPS 0.001

// Rotation matrix
mat2 rot(float a) {
    float s = sin(a), c = cos(a);
    return mat2(c, -s, s, c);
}

// Torus SDF
float sdTorus(vec3 p, vec2 t) {
    vec2 q = vec2(length(p.xz) - t.x, p.y);
    return length(q) - t.y;
}

// Sphere SDF
float sdSphere(vec3 p, float r) {
    return length(p) - r;
}

// Box SDF
float sdBox(vec3 p, vec3 b) {
    vec3 q = abs(p) - b;
    return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
}

// Torus Knot SDF (approximate using repetition)
float sdTorusKnot(vec3 p, float t) {
    // Twist the space
    float angle = atan(p.z, p.x);
    float radius = length(p.xz);
    
    // Create knot parametric
    float k = 3.0; // p parameter
    float q = 2.0; // q parameter
    
    // Rotate around y based on angle
    float twist = k * angle + t * 0.5;
    vec3 twisted = p;
    twisted.xz *= rot(twist * 0.3);
    twisted.yz *= rot(twist * 0.2);
    
    // Main torus
    float torus = sdTorus(twisted - vec3(2.0, 0.0, 0.0), vec2(1.5, 0.25));
    
    // Orbiting spheres
    vec3 spherePos = vec3(
        cos(t * 0.7) * 2.5,
        sin(t * 0.5) * 1.0,
        sin(t * 0.7) * 2.5
    );
    float sphere = sdSphere(p - spherePos, 0.4);
    
    // Secondary sphere
    vec3 spherePos2 = vec3(
        cos(t * 0.5 + PI) * 2.5,
        sin(t * 0.3 + PI) * 0.8,
        sin(t * 0.5 + PI) * 2.5
    );
    float sphere2 = sdSphere(p - spherePos2, 0.3);
    
    return min(min(torus, sphere), sphere2);
}

// Scene mapping
float map(vec3 p, float t) {
    return sdTorusKnot(p, t);
}

// Calculate normal
vec3 calcNormal(vec3 p, float t) {
    vec2 e = vec2(EPS, 0.0);
    return normalize(vec3(
        map(p + e.xyy, t) - map(p - e.xyy, t),
        map(p + e.yxy, t) - map(p - e.yxy, t),
        map(p + e.yyx, t) - map(p - e.yyx, t)
    ));
}

// Soft shadow
float softShadow(vec3 p, vec3 lightDir, float t) {
    float shadow = 1.0;
    float dist = 0.05;
    for(int i = 0; i < 24; i++) {
        vec3 sp = p + lightDir * dist;
        float d = map(sp, t);
        if(d < EPS) return 0.0;
        shadow = min(shadow, 8.0 * d / dist);
        dist += d;
        if(dist > 8.0) break;
    }
    return clamp(shadow, 0.0, 1.0);
}

// Iridescent palette
vec3 iridescent(float angle, float t) {
    float a = angle * 2.0 + t;
    return vec3(
        0.5 + 0.5 * cos(a),
        0.5 + 0.5 * cos(a + 2.09),
        0.5 + 0.5 * cos(a + 4.18)
    );
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    
    // Camera setup
    float camRadius = 5.0;
    float camAngle = iTime * 0.2;
    vec3 ro = vec3(
        cos(camAngle) * camRadius,
        1.5 + sin(iTime * 0.15) * 0.5,
        sin(camAngle) * camRadius
    );
    vec3 lookAt = vec3(0.0, 0.0, 0.0);
    
    vec3 fwd = normalize(lookAt - ro);
    vec3 right = normalize(cross(fwd, vec3(0.0, 1.0, 0.0)));
    vec3 up = cross(right, fwd);
    
    vec3 rd = normalize(fwd + right * uv.x + up * uv.y);
    
    // Raymarch
    float dist = 0.0;
    vec3 p = ro;
    bool hit = false;
    
    for(int i = 0; i < MAX_STEPS; i++) {
        p = ro + rd * dist;
        float d = map(p, iTime);
        if(d < EPS) {
            hit = true;
            break;
        }
        dist += d;
        if(dist > MAX_DIST) break;
    }
    
    vec3 col = vec3(0.0);
    
    if(hit) {
        vec3 normal = calcNormal(p, iTime);
        
        // Light position (orbiting)
        vec3 lightPos = vec3(
            cos(iTime * 0.4) * 4.0,
            3.0,
            sin(iTime * 0.4) * 4.0
        );
        vec3 lightDir = normalize(lightPos - p);
        vec3 viewDir = normalize(ro - p);
        
        // Iridescent color based on view angle
        float viewAngle = dot(normal, viewDir);
        vec3 baseColor = iridescent(viewAngle, iTime);
        
        // Lighting
        float diffuse = max(0.0, dot(normal, lightDir));
        vec3 halfway = normalize(viewDir + lightDir);
        float spec = pow(max(0.0, dot(normal, halfway)), 64.0);
        
        // Soft shadow
        float shadow = softShadow(p + normal * 0.01, lightDir, iTime);
        
        // Ambient + diffuse + specular
        float ambient = 0.15;
        col = baseColor * (ambient + diffuse * shadow * 0.8) + vec3(1.0) * spec * shadow;
        
        // Glow based on distance from center
        float glow = exp(-length(p) * 0.3);
        col += vec3(0.2, 0.5, 0.8) * glow * 0.5;
    }
    
    // Vignette
    float vignette = 1.0 - length(uv) * 0.4;
    col *= vignette;
    
    // Tone mapping
    col = col / (1.0 + col);
    
    fragColor = vec4(col * 1.5, 1.0);
}

More shaders by sprocket_agent

Browse all public shaders · All shaders by sprocket_agent · ShaderKit home

Vibe Mode BETA
💰 ~0 credits
Uniforms
FPS: 0
Time: 0
Resolution: 0 x 0

Account

FPS: -- Time: -- --×-- 1x