Geometric Shadows

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

3D raymarched scene with box, spheres, 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)

// Geometric Shadows - 3D scene with raymarched shadows
#define PI 3.14159265359
#define MAX_STEPS 100
#define MAX_DIST 30.0
#define EPS 0.001

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

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

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

// Scene map
float map(vec3 p) {
    // Ground plane
    float ground = p.y + 1.0;
    
    // Central box
    vec3 boxP = p - vec3(0.0, -0.2, 0.0);
    boxP.xz *= rot(iTime * 0.2);
    float box = sdBox(boxP, vec3(0.6, 0.6, 0.6));
    
    // Left sphere
    float s1 = sdSphere(p - vec3(-1.5, -0.3, 1.0), 0.7);
    
    // Right sphere
    float s2 = sdSphere(p - vec3(1.8, -0.2, -0.5), 0.5);
    
    return min(min(min(ground, box), s1), s2);
}

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

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

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    float t = iTime;
    
    // Camera
    float camAngle = t * 0.1;
    vec3 ro = vec3(cos(camAngle) * 5.0, 3.0, sin(camAngle) * 5.0);
    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);
        if(d < EPS) {
            hit = true;
            break;
        }
        dist += d;
        if(dist > MAX_DIST) break;
    }
    
    vec3 col = vec3(0.0);
    
    if(hit) {
        vec3 normal = calcNormal(p);
        vec3 viewDir = normalize(ro - p);
        
        // Light from upper left
        vec3 lightDir = normalize(vec3(1.0, 1.5, 0.5));
        
        // Color based on object
        vec3 baseColor;
        float ground = p.y + 1.0;
        vec3 boxP = p - vec3(0.0, -0.2, 0.0);
        boxP.xz *= rot(t * 0.2);
        float box = sdBox(boxP, vec3(0.6, 0.6, 0.6));
        
        if(abs(ground) < EPS * 10.0) {
            baseColor = vec3(0.3, 0.35, 0.4); // Ground
        } else if(box < EPS * 10.0) {
            baseColor = vec3(0.8, 0.4, 0.3); // Box (orange)
        } else {
            baseColor = vec3(0.3, 0.6, 0.8); // Spheres (blue)
        }
        
        // Lighting
        float diffuse = max(0.0, dot(normal, lightDir));
        vec3 halfway = normalize(viewDir + lightDir);
        float spec = pow(max(0.0, dot(normal, halfway)), 32.0);
        
        // Soft shadow
        float shadow = softShadow(p + normal * 0.01, lightDir);
        
        float amb = 0.15;
        col = baseColor * (amb + diffuse * shadow * 0.7) + vec3(1.0) * spec * shadow * 0.3;
    } else {
        // Sky gradient
        col = vec3(0.1, 0.15, 0.25) * (1.0 - uv.y * 0.3);
    }
    
    // Vignette
    col *= 1.0 - length(uv) * 0.35;
    
    // Boost
    col *= 1.4;
    
    fragColor = vec4(col, 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