Project_2026-03-04_16-16-28

GLSL shader by scry · created 2026-03-05 · updated 2026-03-06 · 10s loop · 2 passes

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
vec3 cs = vec3(1.,2.,3.);
mat2 r2d(float a) {
    return mat2(cos(a),sin(a),-sin(a),cos(a));
}

// Convert Cartesian to log-spherical coordinates
// Returns vec3(log(r), theta, phi)
vec3 toLogSpherical(vec3 p) {
    float r = length(p);
    float theta = atan(p.y, p.x);
    float phi = acos(p.z / (r + 0.0001));
    return vec3(log(r + 0.0001), theta, phi);
}

// Convert log-spherical back to Cartesian
vec3 fromLogSpherical(vec3 ls) {
    float r = exp(ls.x);
    float theta = ls.y;
    float phi = ls.z;
    return vec3(
        r * sin(phi) * cos(theta),
        r * sin(phi) * sin(theta),
        r * cos(phi)
    );
}

// Adjust distance field for log-spherical space
float adjustDistanceLogSpherical(float d, vec3 p) {
    return d * length(p);
}

// SDF Box
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);
}

Buffer A (iChannel0)


vec4 map(vec3 p) {
    p.y -= .5;
    
    p.zy *= r2d(sin(time*0.5)*0.45+deg*-20.);
    p.xz *= r2d(time*0.25+deg*45.);
    
    vec3 sp = (fract(p-0.)-0.5);
    //p.zy *= r2d(time);//at this angle it disappears, at 5.7 its fine
    p.y *= 2.;
    //p.xz *= r2d(deg*45.);
    //p.yx *= r2d(time);
    // Log-space tiling density
    // scaleRatio = how much bigger each shell is than the next.
    // 4.0 means each smaller box fits in 1/4 the size of the previous.
    float scaleRatio = 1.6;
    // s is derived so that each tile boundary corresponds to exactly scaleRatio
    float rs = log(scaleRatio);  // tiling period in log-space
    float s = 1.0 / rs;          // density = tiles per unit in log-space

    // xzScale: independent scaling ratio for XZ per shell.
    // 1.0 = XZ shrinks at same rate as Y shells.
    // >1.0 = XZ shrinks faster (boxes get proportionally narrower).
    // <1.0 = XZ shrinks slower (boxes get proportionally wider).
    // e.g. 0.5 means XZ only shrinks by sqrt(scaleRatio) per shell
    //      2.0 means XZ shrinks by scaleRatio^2 per shell
    float xzScale = 1.66;
    float stime = -time*1.4962;
    // Only render for p.y < 0 (bottom half); return large distance for top
    //if (p.y > 0.0) return vec4(0.0, 0.0, 0.0, 10.0);
    if (p.y > 1.5) {return vec4(vec3(-1.),length(sp)-0.1);}
    if (p.y > 0.) {return vec4(0.,0.,0.,5.);}
    float ly = log(-p.y + 0.0001)/(1.+s*0.2);
    // Animate: shift in log-space = zoom in linear space
    ly += stime;

    // Apply ySpacing to the tiling density, not to ly itself.
    // Tile index using the modified density
    float si = floor(ly * s - 0.5) - stime * s;

    // Local coordinate within tile
    float localY = (fract(ly * s - 0.5) - 0.5) * rs;

    // tileScale: the linear-space radius at the center of this tile.
    // Each tile spans rs in log-space, so the scale factor per tile
    // is exp(rs) = scaleRatio, and the center of tile si is at exp((si+0.5)*rs).
    float yTileScale = exp((si + 0.5) * rs);

    // XZ scale per shell: independent from Y tiling.
    // xzTileScale shrinks XZ by scaleRatio^xzScale per shell.
    float xzTileScale = exp((si + 0.5) * rs * xzScale);

    // Build the local-space point:
    // localY is already in log-tile space, p.xz need to be divided by tileScale
    // so the box has consistent proportions across shells
    // No sign flip needed — we're only in the p.y < 0 region
    vec3 lp = vec3(p.x / xzTileScale, localY, p.z / xzTileScale);

    for (int i=0;i<3;i++) {
        lp = abs(lp)-0.056;
    }

    float d = sdBox(lp, vec3(0.04))-0.015;
    // Correct distance back to linear space using the smaller of the two scales
    // (conservative estimate so raymarcher doesn't overshoot)
    d *= min(yTileScale, xzTileScale);
    //d = min(d,length(sp)-0.1);
    return vec4(vec3(si), d-0.01);
}

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

vec2 RM(vec3 ro, vec3 rd) {
    float dO = 0.0;
    float ii = 0.0;
    for (int i=0; i<1600; i++) {
        vec3 p = ro + rd*dO;
        float dS = map(p).w/24.;
        dO += dS;
        ii += 1.0; // Can be used for effects based on steps
        if (dO > 20.0 || dS < 0.0001) break;
    }
    return vec2(dO, ii);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord.xy / iResolution.xy;
    vec2 tv = uv;
    uv -= 0.5;
    uv.x *= ar;
    vec3 col = vec3(0.);
    vec3 ro = vec3(0.0, 0.0, 5.); // Ray Origin
    vec3 rd = normalize(vec3(uv, -1.0)); // Ray Direction

    // Point light at the convergence center of the cubes
    // Place it at the actual scene center where cubes converge (origin, offset by the p.y -= .5)
    vec3 lightPos = vec3(0.0, 0.5, 0.0);
    vec3 lightColor = vec3(1.0, 0.95, 0.8) * 8.0;

    // Multi-bounce reflections
    vec3 accumCol = vec3(0.0);
    float accumRef = 1.0;
    const int MAX_BOUNCES = 4;
    vec2 d = RM(ro,rd);
    for (int bounce = 0; bounce < MAX_BOUNCES; bounce++) {
        vec2 res = RM(ro, rd);
        float hitT = res.x;
        vec3 hitP = ro + rd * hitT;

        if (hitT >= 20.0) {
            // Sky / background
            accumCol += accumRef * vec3(0.0);
            break;
        }

        vec3 n = calcNormal(hitP);
        vec3 viewDir = -rd;

        // Point light shading
        vec3 toLight = lightPos - hitP;
        float lightDist = length(toLight);
        vec3 lightDir = toLight / lightDist;
        float atten = 1.0 / (0.5 + 0.3 * lightDist * lightDist);

        float diff = max(dot(n, lightDir), 0.0) * atten;
        float spec = pow(max(dot(reflect(-lightDir, n), viewDir), 0.0), 32.0) * atten;

        vec4 mapVal = map(hitP);
        float si = mapVal.x;
        bool isSphere = (si < -0.5); // tagged with -1 from the y>0 branch

        vec3 tint;
        float reflectivity;
        if (isSphere) {
            // Metallic mirror spheres
            tint = vec3(0.8, 0.82, 0.85); // silver/chrome base
            reflectivity = 0.85;
        } else {
            tint = 0.5 + 0.5 * sin(cs + si * 0.7);
            reflectivity = 0.45;
        }
        float fres = isSphere ? pow(1.0 - max(dot(n, viewDir), 0.0), 5.0) : 0.0;
        reflectivity = mix(reflectivity, 1.0, fres);
        vec3 localCol = tint * (vec3(0.05) + lightColor * 0.1 * diff) + lightColor * 0.05 * spec;
        accumCol += accumRef * (1.0 - reflectivity) * localCol;
        accumRef *= reflectivity;

        // Set up next bounce
        ro = hitP + n * 0.005;
        rd = reflect(rd, n);
    }
    
    col = accumCol;

    // Screen-space glow for the light source itself
    vec3 camPos = vec3(0.0, 0.0, 5.0);
    vec3 lDir = lightPos - camPos;
    vec2 lightUV = lDir.xy / (-lDir.z) * 1.0;
    float glowDist = length(uv - lightUV);
    vec3 addlight = vec3(0.);
    addlight += lightColor * 0.02 * exp(-glowDist * glowDist * 10.0);
    uv.y += -0.1;
    addlight += exp(1.-length(uv)*10.)*0.2;
    for (int i=0;i<31;i++) {
        addlight += exp(1.-length(uv)*20.)*0.01*(sin(atan(uv.x,uv.y)*(40.+float(i-20))+time*10.*sin(float(i)*pi/2.)+sin(atan(uv.x,uv.y)*8.+sin(float(i))*14.))*0.5+0.5);
    }
    //if (d.x > 4.) {
        col += addlight*smoothstep(0.0,2.,d.x-3.6);
    //}
    if (d.x > 10.) {
        col += sin(d.y*0.1+cs)*0.05;
    }
    fragColor = vec4(col, 1.0);
}

Image

Not used

More shaders by scry

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

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

Account

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