Project_2026-02-13_16-02-06
GLSL shader by scry · created 2026-02-14 · 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. //1 degree
#define time iTime*2.*pi/10. //sin(time) loops 10 seconds
#define R iResolution.xy //shorthand
#define ar R.x/R.y //aspect ratio
#define M iMouse //shorthand
#define xm (M.xy/R) //normalized mouse
#define nm ((xm.xy-0.5)*vec2(ar,1.)+0.5) //aspect ratio correction
vec3 cs = vec3(1.,2.,3.);
mat2 r2d(float a) {
return mat2(cos(a),sin(a),-sin(a),cos(a));
}
vec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d) {
return a + b * cos(6.28318 * (c * t + d));
}
vec3 paletteClassic(float t) {
return palette(t, vec3(0.5), vec3(0.5), vec3(1.0), vec3(0.0, 0.33, 0.67));
}
Buffer A (iChannel0)
// Mandelbox distance estimator
// Parameters
const float SCALE = -1.5;
const float FIXED_RADIUS2 = 1.0;
const float MIN_RADIUS2 = 0.25;
const int MB_ITERS = 12;
void sphereFold(inout vec3 z, inout float dz) {
float r2 = dot(z, z);
if (r2 < MIN_RADIUS2) {
float temp = FIXED_RADIUS2 / MIN_RADIUS2;
z *= temp;
dz *= temp;
} else if (r2 < FIXED_RADIUS2) {
// Clamp r2 away from zero to prevent division explosion
float temp = FIXED_RADIUS2 / max(r2, 1e-6);
z *= temp;
dz *= temp;
}
}
void boxFold(inout vec3 z) {
z = clamp(z, -1.0, 1.0) * 2.0 - z;
}
vec2 mandelbox(vec3 p) {
float s = 0.2;
p = fract(p*s)-0.5;
p /= s;
vec3 z = p;
float dz = 1.0;
float trap = 1e10;
for (int i = 0; i < MB_ITERS; i++) {
boxFold(z);
sphereFold(z, dz);
z = SCALE * z + p;
dz = dz * abs(SCALE) + 1.0;
trap = min(trap, dot(z, z));
}
float r = length(z);
float d = r / abs(dz);
// Clamp DE to a small positive floor to prevent zero/negative steps
return vec2(max(d, 1e-7), trap);
}
vec4 map(vec3 p) {
//p = fract(p)-0.5;
vec2 mb = mandelbox(p);
return vec4(p, mb.x);
}
vec2 RM(vec3 ro, vec3 rd) {
float dO = 0.0;
float trap = 0.0;
for (int i = 0; i < 120; i++) {
vec3 pos = ro + rd * dO;
vec2 mb = mandelbox(pos);
float dS = mb.x;
trap = mb.y;
if (dO > 30.0) break;
// Scale hit threshold with accumulated distance
float hitThresh = max(0.0002 * dO, 5e-6);
if (dS < hitThresh) { break; }
// Clamp step to avoid overstepping; use conservative factor
dO += dS * 0.7;
}
return vec2(dO, trap);
}
vec3 getNormal(vec3 p) {
// Scale epsilon with distance to keep normals stable at all distances
// Use the DE value itself to pick a good epsilon
float d = mandelbox(p).x;
float eps = max(5e-5, clamp(d * 0.5, 1e-5, 0.002));
vec2 e = vec2(eps, 0.0);
vec3 n = vec3(
mandelbox(p + e.xyy).x - mandelbox(p - e.xyy).x,
mandelbox(p + e.yxy).x - mandelbox(p - e.yxy).x,
mandelbox(p + e.yyx).x - mandelbox(p - e.yyx).x
);
return normalize(n + 1e-10); // prevent zero-length normal
}
float calcAO(vec3 p, vec3 n) {
float occ = 0.0;
float sca = 1.0;
for (int i = 0; i < 5; i++) {
float h = 0.01 + 0.12 * float(i);
float d = mandelbox(p + h * n).x;
occ += (h - d) * sca;
sca *= 0.7;
}
return clamp(1.0 - 3.0 * occ, 0.0, 1.0);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
vec2 p = (2.0 * fragCoord - iResolution.xy) / iResolution.y;
p /= 4.-log(length(uv-0.5)+2.)*4.;
// Smooth continuous orbit around a single Mandelbox cell center
// The fract(p*0.2)-0.5 repetition means cell center is at origin,
// cell extends roughly -2.5 to 2.5. We orbit just inside that.
float t = iTime * 0.2;
// Lissajous-style orbit: three incommensurate frequencies
// so the path never exactly repeats and stays smooth forever
float orbitR = 0.8; // radius - close to surface but not inside
float bobR = 0.6; // vertical bob amplitude
vec3 ro = vec3(
orbitR * cos(t * 0.7) + 0.4 * sin(t * 0.31),
bobR * sin(t * 0.53) + 0.3 * cos(t * 0.79),
orbitR * sin(t * 0.7) + 0.4 * cos(t * 0.43)
);
// Look-at: slightly ahead on the same path (derivative-ish)
float dt = 0.15;
float t2 = t + dt;
vec3 roAhead = vec3(
orbitR * cos(t2 * 0.7) + 0.3 * sin(t2 * 0.31),
bobR * sin(t2 * 0.53) + 0.2 * cos(t2 * 0.79),
orbitR * sin(t2 * 0.7) + 0.3 * cos(t2 * 0.43)
);
vec3 ta = roAhead;
// Mouse control
if (M.z > 0.0) {
float mx = M.x / R.x * 2.0 * pi;
float my = (M.y / R.y - 0.5) * pi;
float camDist = 2.0;
vec3 ctr = ro;
ro = ctr + vec3(camDist * cos(mx) * cos(my), camDist * sin(my), camDist * sin(mx) * cos(my));
ta = ctr;
}
vec3 ww = normalize(ta - ro);
vec3 upVec = vec3(0.0, 1.0, 0.0);
if (abs(dot(ww, upVec)) > 0.999) upVec = vec3(1.0, 0.0, 0.0);
vec3 uu = normalize(cross(ww, upVec));
vec3 vv = normalize(cross(uu, ww));
// Gentle banking roll from lateral acceleration
float rollAngle = 0.15 * sin(t * 0.7) * cos(t * 0.53);
vec2 pr = p * r2d(rollAngle);
vec3 rd = normalize(pr.x * uu + pr.y * vv + 1.5 * ww);
// Raymarch
vec2 res = RM(ro, rd);
float dist = res.x;
float trap = res.y;
vec3 col = vec3(0.02, 0.02, 0.04);
if (dist < 30.0) {
vec3 hp = ro + rd * dist;
// Push hit point slightly along ray to ensure we're on the surface, not inside
float surfDE = mandelbox(hp).x;
hp += rd * surfDE * 0.5;
vec3 n = getNormal(hp);
float offsetDist = max(0.005, 0.002 * dist);
float ao = calcAO(hp + n * offsetDist, n);
// Two-light setup for more natural shading
vec3 l1 = normalize(vec3(0.8, 0.6, -0.5));
vec3 l2 = normalize(vec3(-0.4, 0.3, 0.7));
float diff1 = max(dot(n, l1), 0.0);
float diff2 = max(dot(n, l2), 0.0);
float diff = diff1 * 0.8 + diff2 * 0.3;
// Fresnel-attenuated specular to reduce artifacts at grazing angles
float fresnel = 1.0 - abs(dot(n, -rd));
fresnel = fresnel * fresnel * fresnel;
vec3 h = normalize(l1 + (-rd));
float spec = pow(max(dot(n, h), 0.0), 32.0);
// Color from orbit trap
float trapT = sqrt(trap) * 0.4;
vec3 matCol = palette(trapT,
vec3(0.4, 0.35, 0.3),
vec3(0.3, 0.25, 0.25),
vec3(1.0, 1.0, 1.0),
vec3(0.0, 0.1, 0.2));
col = matCol * (0.2 + 0.8 * diff) * ao;
col += vec3(0.6) * spec * 0.15 * ao * (1.0 - fresnel * 0.8);
float fog = exp(-0.03 * dist * dist);
col = mix(vec3(0.02, 0.02, 0.04), col, fog);
}
// Tonemap and gamma
col = col / (1.0 + col);
col = pow(col, vec3(0.4545));
fragColor = vec4(col, 1.0);
}
Image
Not used
More shaders by scry
- Project_2026-08-15_18-22-06
- Project_2026-08-14_16-45-00
- Project_2026-07-24_15-10-23
- Project_2026-07-15_03-18-26
- Glass of Orange Juice (raymarched)
- Project_2026-07-13_00-53-50
- Project_2026-07-10_16-39-54
- Popcorn ceiling
- Project_2026-06-20_15-32-19
- Project_2026-06-25_12-22-43
- Visual Enhancer
- Project_2026-06-24_11-11-39
Browse all public shaders · All shaders by scry · ShaderKit home
Unread comments
×Projects with new comments. Click to open and read.