My attempt at the Amiga Boing Ball Demo
Log in to post a comment.
#version 300 es
precision highp float;
uniform float iTime;
uniform vec2 iResolution;
out vec4 fragColor;
#define PI 3.14159265359
#define MAX_STEPS 100
#define MAX_DIST 50.0
#define SURF_DIST 0.001
// helpers
float tri(float t) { return abs(mod(t, 2.0) - 1.0); }
float bounce(float t) { return 4.0 * t * (1.0 - t); }
float sdSphere(vec3 p, vec3 c, float r) { return length(p - c) - r; }
float sdPlane(vec3 p) { return p.y + 1.0; }
// scene map
float mapScene(vec3 p, out int objID, float time) {
float d = MAX_DIST;
objID = -1;
float hx = tri(time * 0.5); // horizontal motion
float by = fract(time * 0.7); // vertical phase
float hy = bounce(by); // parabolic bounce
vec3 center = vec3(
mix(-1.5, 1.5, hx),
mix(-0.5, 1.5, hy),
0.0
);
float ds = sdSphere(p, center, 0.75);
d = ds; objID = 1;
// finite floor (disk radius = 5.0)
float floorRadius = 5.0;
if (length(p.xz) < floorRadius) {
float dp = sdPlane(p);
if (dp < d) { d = dp; objID = 0; }
}
return d;
}
// normal
vec3 getNormal(vec3 p, float time) {
vec2 e = vec2(0.001, 0.0);
int tmp;
return normalize(vec3(
mapScene(p + e.xyy, tmp, time) - mapScene(p - e.xyy, tmp, time),
mapScene(p + e.yxy, tmp, time) - mapScene(p - e.yxy, tmp, time),
mapScene(p + e.yyx, tmp, time) - mapScene(p - e.yyx, tmp, time)
));
}
// ray march
vec3 rayMarch(vec3 ro, vec3 rd, out int objID, float time) {
float t = 0.0;
objID = -1;
for (int i = 0; i < MAX_STEPS; i++) {
vec3 p = ro + rd * t;
float dist = mapScene(p, objID, time);
if (dist < SURF_DIST) return p;
if (t > MAX_DIST) break;
t += dist;
}
objID = -1;
return ro + rd * t;
}
// phong + sky
vec3 phong(vec3 p, vec3 n, vec3 v) {
vec3 Lpos = vec3(5.0, 5.0, -5.0);
vec3 L = normalize(Lpos - p);
float dif = max(dot(n, L), 0.0);
vec3 R = reflect(-L, n);
float spec = pow(max(dot(R, v), 0.0), 32.0);
return vec3(0.2) + dif * vec3(1.0) + spec * vec3(1.0);
}
vec3 skyColor(vec3 rd) {
float t = clamp(rd.y * 0.5 + 0.5, 0.0, 1.0);
vec3 hor = vec3(0.8, 0.9, 1.0);
vec3 zen = vec3(0.2, 0.5, 0.9);
return mix(hor, zen, t);
}
// rotating checker (seamless reversal + 30° tilt)
vec3 checker(vec3 p, vec3 center, float time) {
vec3 d = normalize(p - center);
// 30° tilt around Z (sideways)
float tilt = PI / 6.0;
float ct = cos(tilt), st = sin(tilt);
mat3 rotZ = mat3(
ct, -st, 0.0,
st, ct, 0.0,
0.0, 0.0, 1.0
);
d = rotZ * d;
// compute spin angle with smooth direction reversal
float phase = time * 0.5; // same as horizontal motion
float cycle = floor(phase); // counts half-cycles
float localT = fract(phase); // 0–1 inside half-cycle
float dir = mod(cycle, 2.0) < 1.0 ? 1.0 : -1.0;
float spinSpeed = 4.0; // tweak for visual speed
float spin = spinSpeed * (dir * localT + cycle); // continuous angle
// spin around Y
float cs = cos(spin), sn = sin(spin);
mat3 rotY = mat3(
cs, 0.0, -sn,
0.0, 1.0, 0.0,
sn, 0.0, cs
);
d = rotY * d;
// spherical UV → checker
float u = atan(d.z, d.x) / (2.0 * PI) + 0.5;
float v = acos(d.y) / PI;
float tile = mod(floor(u * 8.0) + floor(v * 8.0), 2.0);
return mix(vec3(1.0, 0.0, 0.0), vec3(1.0), tile);
}
void main() {
float time = iTime * 0.5; // slower motion
// camera
vec2 uv = (gl_FragCoord.xy / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
vec3 ro = vec3(0.0, 0.3, -4.2);
float focalLength = 3.2;
vec3 rd = normalize(vec3(uv, focalLength));
int obj;
vec3 hit = rayMarch(ro, rd, obj, time);
vec3 vDir = normalize(-rd);
vec3 col;
if (obj == 1) {
vec3 n = getNormal(hit, time);
float hx = tri(time * 0.5);
float by = fract(time * 0.7);
float hy = bounce(by);
vec3 center = vec3(
mix(-1.5, 1.5, hx),
mix(-0.5, 1.5, hy),
0.0
);
vec3 base = checker(hit, center, time);
col = base * phong(hit, n, vDir);
}
else if (obj == 0) {
vec3 n = vec3(0.0, 1.0, 0.0);
float c = mod(floor(hit.x) + floor(hit.z), 2.0);
vec3 base = mix(vec3(0.1), vec3(0.8), c);
col = phong(hit, n, vDir) * base;
}
else {
col = skyColor(rd);
}
fragColor = vec4(col, 1.0);
}