export const PARAMS = {
blend: { value: 0.4, min: 0.05, max: 1.5, step: 0.01, label: "Blend Smoothness", folder: "Structure" },
twist: { value: 0.4, min: 0, max: 2.5, step: 0.01, label: "Domain Twist", folder: "Structure" },
cameraDist:{ value: 4.0, min: 2, max: 10, step: 0.05, label: "Camera Distance", folder: "Structure" },
speed: { value: 0.2, min: 0, max: 1.5, step: 0.01, label: "Orbit Speed", folder: "Behavior" },
hue: { value: 200, min: 0, max: 360, step: 1, label: "Hue", folder: "Appearance" },
glow: { value: 0.4, min: 0, max: 1.5, step: 0.01, label: "Glow", folder: "Appearance" },
};
export const SHARE = { bookmarked: ["blend", "twist", "cameraDist", "speed", "hue", "glow"] };
export function shaderUniforms(params) {
return {
u_blend: { value: params.blend },
u_twist: { value: params.twist },
u_cameraDist: { value: params.cameraDist },
u_speed: { value: params.speed },
u_hue: { value: params.hue },
u_glow: { value: params.glow },
};
}
export function shaderAnimate(uniforms, params) {
uniforms.u_blend.value = params.blend;
uniforms.u_twist.value = params.twist;
uniforms.u_cameraDist.value = params.cameraDist;
uniforms.u_speed.value = params.speed;
uniforms.u_hue.value = params.hue;
uniforms.u_glow.value = params.glow;
}
export function fragmentShader() {
return `
uniform float u_time, u_blend, u_twist, u_cameraDist, u_speed, u_hue, u_glow;
uniform vec2 u_resolution;
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);
}
float sdTorus(vec3 p, vec2 t) {
vec2 q = vec2(length(p.xz) - t.x, p.y);
return length(q) - t.y;
}
float smin(float a, float b, float k) {
float h = max(k - abs(a - b), 0.0) / k;
return min(a, b) - h * h * k * 0.25;
}
// Domain twist around Y axis
vec3 twist(vec3 p, float k) {
float c = cos(k * p.y);
float s = sin(k * p.y);
return vec3(c * p.x - s * p.z, p.y, s * p.x + c * p.z);
}
float scene(vec3 p) {
vec3 q = twist(p, u_twist);
float s = sdSphere(q - vec3(0.4, 0.0, 0.0), 0.55);
float b = sdBox(q + vec3(0.4, 0.0, 0.0), vec3(0.45));
float t = sdTorus(q, vec2(0.85, 0.18));
return smin(smin(s, b, u_blend), t, u_blend);
}
vec3 calcNormal(vec3 p) {
const float h = 0.0005;
vec2 e = vec2(1.0, -1.0) * 0.5773;
return normalize(
e.xyy * scene(p + e.xyy * h) +
e.yyx * scene(p + e.yyx * h) +
e.yxy * scene(p + e.yxy * h) +
e.xxx * scene(p + e.xxx * h)
);
}
vec3 palette(float t, float baseHue) {
vec3 a = vec3(0.5), b = vec3(0.5), c = vec3(1.0);
vec3 d = vec3(0.0, 0.10, 0.20) + baseHue / 360.0;
return a + b * cos(6.2831853 * (c * t + d));
}
void main() {
vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution) / u_resolution.y;
float a = u_time * u_speed;
vec3 ro = vec3(u_cameraDist * cos(a), 0.5, u_cameraDist * sin(a));
vec3 ww = normalize(-ro);
vec3 uu = normalize(cross(vec3(0.0, 1.0, 0.0), ww));
vec3 vv = cross(ww, uu);
vec3 rd = normalize(uv.x * uu + uv.y * vv + 1.5 * ww);
float t = 0.0;
float steps = 0.0;
bool hit = false;
for (int i = 0; i < 96; i++) {
vec3 p = ro + rd * t;
float d = scene(p);
if (d < 0.001) { hit = true; break; }
if (t > 12.0) break;
t += d;
steps += 1.0;
}
vec3 col = vec3(0.02, 0.02, 0.04);
if (hit) {
vec3 p = ro + rd * t;
vec3 n = calcNormal(p);
vec3 lightDir = normalize(vec3(0.6, 0.7, 0.4));
float diff = max(dot(n, lightDir), 0.0);
float spec = pow(max(dot(reflect(-lightDir, n), -rd), 0.0), 32.0);
vec3 base = palette(0.5 + 0.3 * dot(n, vec3(0.0, 1.0, 0.0)), u_hue);
col = base * (0.25 + 0.75 * diff) + vec3(spec) * 0.5;
}
col += u_glow * vec3(steps / 96.0) * palette(steps / 96.0, u_hue);
col = pow(col, vec3(1.0 / 2.2));
gl_FragColor = vec4(col, 1.0);
}
`;
}