Back to shaders
Shader test bench
20260512
runnable fragment
Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.
Code
precision mediump float;
#define TDOutputSwizzle(c) (c)
// ===============================================================
// ABYSSAL VEIL JELLY — deep-sea shader creature
// 3D raymarched translucent bell + procedural oral arms + veil
// TouchDesigner GLSL TOP · 1080x1920 (Reels)
// Keeps original color constants exactly.
// ===============================================================
#define fragColor gl_FragColor
uniform float iTime;
#define MAX_STEPS 80
#define MAX_DIST 9.0
#define SURF_DIST 0.0012
#define TAU 6.28318530718
#define PI 3.14159265359
#define INTENSITY 1.8
#define GLOW_POWER 2.1
#define VEIL_POWER 2.0
const vec3 ACID = vec3(0.0, 1.0, 0.624);
const vec3 CYAN = vec3(0.0, 0.812,1.0);
const vec3 VIOLET = vec3(0.545,0.0, 1.0);
const vec3 PINK = vec3(1.0, 0.0, 0.431);
// ---------- math helpers ----------
float hash11(float n) {
return fract(sin(n * 12.9898) * 43758.5453);
}
float hash13(vec3 p) {
return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453);
}
float noise3(vec3 x) {
vec3 p = floor(x);
vec3 f = fract(x);
f = f * f * (3.0 - 2.0 * f);
return mix(
mix(
mix(hash13(p + vec3(0, 0, 0)), hash13(p + vec3(1, 0, 0)), f.x),
mix(hash13(p + vec3(0, 1, 0)), hash13(p + vec3(1, 1, 0)), f.x),
f.y
),
mix(
mix(hash13(p + vec3(0, 0, 1)), hash13(p + vec3(1, 0, 1)), f.x),
mix(hash13(p + vec3(0, 1, 1)), hash13(p + vec3(1, 1, 1)), f.x),
f.y
),
f.z
);
}
float fbm(vec3 p) {
float v = 0.0;
float a = 0.5;
for (int i = 0; i < 5; i++) {
v += a * noise3(p);
p *= 2.03;
a *= 0.52;
}
return v;
}
float sdCapsule(vec3 p, vec3 a, vec3 b, float r) {
vec3 pa = p - a;
vec3 ba = b - a;
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
return length(pa - ba * h) - r;
}
float softUnion(float a, float b, float k) {
float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
return mix(b, a, h) - k * h * (1.0 - h);
}
// ---------- camera ----------
struct Cam {
vec3 ro;
vec3 fwd;
vec3 rgt;
vec3 up;
};
Cam makeCam(float t) {
Cam c;
float a = t * 0.09;
float dist = 3.15;
c.ro = vec3(
sin(a) * dist,
0.15 + sin(t * 0.23) * 0.10,
cos(a) * dist
);
vec3 target = vec3(
0.0,
-0.12 + sin(t * 0.35) * 0.05,
0.0
);
c.fwd = normalize(target - c.ro);
c.rgt = normalize(cross(vec3(0, 1, 0), c.fwd));
c.up = cross(c.fwd, c.rgt);
return c;
}
vec3 creatureOffset(float t) {
return vec3(
sin(t * 0.45) * 0.09,
sin(t * 0.34) * 0.08,
cos(t * 0.39) * 0.07
);
}
// ---------- jelly bell SDF ----------
float bellSDF(vec3 p, float t) {
vec3 q = p - creatureOffset(t);
float pulse = 0.5 + 0.5 * sin(t * 1.55);
q.y += 0.035 * sin(q.x * 2.6 + q.z * 1.8 - t * 1.2);
float radial = length(q.xz);
float dome = length(vec3(
q.x * 1.05,
(q.y - 0.18) * 1.28,
q.z * 1.05
)) - (0.76 + pulse * 0.035);
float lowerCut = q.y + 0.18 + 0.08 * sin(radial * 6.0 - t * 1.4);
float rim = abs(
radial - (0.68 + 0.045 * sin(12.0 * atan(q.z, q.x) + t * 2.0))
) - 0.025;
float rimY = abs(q.y + 0.25 + pulse * 0.035) - 0.035;
float rimBand = max(rim, rimY);
float ribs = sin(
atan(q.z, q.x) * 16.0 + sin(q.y * 4.0 - t) * 0.8
) * 0.008;
float skinNoise = (
fbm(q * 3.2 + vec3(0.0, t * 0.12, 0.0)) - 0.5
) * 0.018;
float shell = max(dome + ribs + skinNoise, -lowerCut);
return min(shell, rimBand);
}
float armsSDF(vec3 p, float t) {
vec3 q = p - creatureOffset(t);
float d = 10.0;
for (int i = 0; i < 10; i++) {
float fi = float(i);
float a = fi / 10.0 * TAU + 0.25 * sin(t * 0.31 + fi);
float rad = 0.10 + 0.025 * sin(fi * 2.1);
vec3 prev = vec3(
cos(a) * rad,
-0.23,
sin(a) * rad
);
for (int j = 1; j <= 8; j++) {
float u = float(j) / 8.0;
float sway = sin(t * 1.2 + u * 5.2 + fi * 0.7) * 0.18 * u;
float twist = a + sway + sin(u * 8.0 + t * 0.6 + fi) * 0.25;
vec3 next = vec3(
cos(twist) * (rad + u * 0.22),
-0.23 - u * 1.42,
sin(twist) * (rad + u * 0.22)
);
next.x += sin(t * 0.72 + u * 4.0 + fi) * 0.09 * u;
next.z += cos(t * 0.66 + u * 4.2 + fi) * 0.09 * u;
float r = mix(0.032, 0.009, u);
d = softUnion(d, sdCapsule(q, prev, next, r), 0.055);
prev = next;
}
}
return d;
}
float creatureSDF(vec3 p, float t) {
float b = bellSDF(p, t);
float a = armsSDF(p, t);
return softUnion(b, a, 0.08);
}
vec3 getNormal(vec3 p, float t) {
float e = 0.002;
vec2 k = vec2(1.0, -1.0);
return normalize(
k.xyy * creatureSDF(p + k.xyy * e, t) +
k.yyx * creatureSDF(p + k.yyx * e, t) +
k.yxy * creatureSDF(p + k.yxy * e, t) +
k.xxx * creatureSDF(p + k.xxx * e, t)
);
}
float rayMarch(vec3 ro, vec3 rd, float t) {
float d = 0.0;
for (int i = 0; i < MAX_STEPS; i++) {
float s = creatureSDF(ro + rd * d, t);
if (s < SURF_DIST || d > MAX_DIST) {
break;
}
d += max(s * 0.72, 0.006);
}
return d;
}
// ---------- 2D helpers ----------
float distSeg(vec2 p, vec2 a, vec2 b) {
vec2 pa = p - a;
vec2 ba = b - a;
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
return length(pa - ba * h);
}
vec2 project(vec3 p, Cam c) {
vec3 rel = p - c.ro;
float zc = max(dot(rel, c.fwd), 0.06);
return vec2(
dot(rel, c.rgt) / zc,
dot(rel, c.up) / zc
);
}
// ---------- water atmosphere ----------
float caustic(vec2 p, float t) {
vec2 q = p * 1.6;
float c = 0.0;
for (int i = 0; i < 4; i++) {
float fi = float(i);
q += 0.28 * vec2(
cos(t * 0.25 + fi * 2.1),
sin(t * 0.18 + fi * 1.7)
);
c += sin(q.x * (2.4 + fi) + t * (0.45 + fi * 0.08))
* cos(q.y * (2.7 + fi) - t * 0.35);
}
return pow(0.5 + 0.5 * c / 4.0, 3.2);
}
float marineSnow(vec2 p, float t) {
float s = 0.0;
for (int i = 0; i < 7; i++) {
float fi = float(i);
vec2 drift = p * (1.0 + fi * 0.55)
+ vec2(
t * 0.025 * (fi + 1.0),
-t * 0.018 * (fi + 0.5)
);
vec2 cell = floor(drift);
vec2 f = fract(drift) - 0.5;
float h = hash11(dot(cell, vec2(41.7, 289.3)) + fi * 19.1);
if (h > 0.982) {
s += smoothstep(0.05, 0.0, length(f)) * (1.0 - fi * 0.09);
}
}
return s;
}
// ---------- procedural veil and tentacles ----------
vec3 renderVeil(vec2 st, Cam cam, float t) {
vec3 col = vec3(0.0);
vec3 off = creatureOffset(t);
// Long transparent rim tentacles.
for (int i = 0; i < 28; i++) {
float fi = float(i);
float a = fi / 28.0 * TAU;
float pulse = 0.5 + 0.5 * sin(t * 1.55);
vec3 prev = off + vec3(
cos(a) * (0.60 + pulse * 0.04),
-0.24,
sin(a) * (0.60 + pulse * 0.04)
);
for (int j = 1; j <= 9; j++) {
float u = float(j) / 9.0;
float wave = sin(t * 0.95 + u * 7.0 + fi * 0.47) * 0.23 * u;
float spiral = a + wave + sin(u * 5.0 + t * 0.37) * 0.12;
vec3 next = off + vec3(
cos(spiral) * (0.60 + 0.10 * sin(u * PI) + 0.14 * u),
-0.24 - u * 1.95,
sin(spiral) * (0.60 + 0.10 * sin(u * PI) + 0.14 * u)
);
next.x += sin(t * 0.51 + fi + u * 4.0) * 0.13 * u;
next.z += cos(t * 0.46 + fi + u * 3.7) * 0.13 * u;
vec2 a2 = project(prev, cam);
vec2 b2 = project(next, cam);
float d = distSeg(st, a2, b2);
float line = smoothstep(0.0028, 0.0, d);
float glow = exp(-d * 85.0) * 0.08;
float fire = pow(
max(sin(u * TAU * 2.0 - t * 2.5 + fi * 0.6), 0.0),
3.0
);
vec3 c = mix(
CYAN,
PINK,
0.5 + 0.5 * sin(fi * 0.9 + u * 2.0)
);
c = mix(c, ACID, fire * 0.55);
col += c
* (line * 0.85 + glow * (1.25 + fire * 1.35))
* (1.0 - u * 0.26)
* VEIL_POWER;
prev = next;
}
}
// Internal radial nerves on the bell.
for (int i = 0; i < 18; i++) {
float fi = float(i);
float a = fi / 18.0 * TAU + sin(t * 0.2) * 0.08;
vec3 p0 = off + vec3(0.0, 0.16, 0.0);
vec3 p1 = off + vec3(
cos(a) * 0.66,
-0.20 + 0.02 * sin(t + fi),
sin(a) * 0.66
);
vec2 a2 = project(p0, cam);
vec2 b2 = project(p1, cam);
float d = distSeg(st, a2, b2);
float nerve = exp(-d * 128.0) * 0.145;
col += mix(VIOLET, CYAN, 0.55) * nerve * GLOW_POWER;
}
return col;
}
// ---------- main ----------
void main() {
vec2 uv = vUV.st;
vec2 st = uv * 2.0 - 1.0;
st.x *= uTDOutputInfo.res.z / uTDOutputInfo.res.w;
float t = iTime;
Cam cam = makeCam(t);
vec3 rd = normalize(
cam.fwd +
cam.rgt * st.x +
cam.up * st.y
);
// ---------- background: abyssal water ----------
vec3 col = vec3(0.004, 0.007, 0.020);
col += vec3(0.025, 0.025, 0.075)
* smoothstep(-0.75, 0.95, st.y);
col += vec3(0.030, 0.095, 0.155)
* caustic(st * 0.75 + vec2(t * 0.03, 0.0), t)
* smoothstep(-0.55, 0.95, st.y);
// ---------- raymarched jelly body ----------
float d = rayMarch(cam.ro, rd, t);
bool hit = d < MAX_DIST;
vec3 p = cam.ro + rd * d;
if (hit) {
vec3 n = getNormal(p, t);
vec3 L1 = normalize(vec3(-0.45, 0.85, -0.25));
vec3 L2 = normalize(vec3(0.55, -0.15, 0.72));
float diff = max(dot(n, L1), 0.0);
float rim = pow(1.0 - max(dot(-rd, n), 0.0), 3.0);
float back = pow(max(dot(L2, rd), 0.0), 2.0);
float scan = 0.5 + 0.5 * sin((p.y + p.x * 0.25) * 18.0 - t * 2.3);
float livingNoise = fbm(p * 5.0 + vec3(0.0, t * 0.2, 0.0));
vec3 body = vec3(0.035, 0.065, 0.13);
body += CYAN * diff * 0.22;
body += VIOLET * 0.20;
body += ACID * back * 0.48;
body += PINK * scan * rim * 0.28;
body += CYAN * rim * (0.64 + livingNoise * 0.36);
float alpha = 0.46 + rim * 0.58 + back * 0.26;
col = mix(col, body, clamp(alpha, 0.0, 0.82));
// Soft internal glow near the bell core.
float core = exp(
-length((p - creatureOffset(t)) * vec3(1.0, 1.8, 1.0)) * 2.1
);
col += mix(
VIOLET,
ACID,
0.35 + 0.35 * sin(t * 1.4)
) * core * 0.42 * GLOW_POWER;
}
// ---------- veil, tentacles, internal nerves ----------
col += renderVeil(st, cam, t);
// ---------- bioluminescent marine snow ----------
float snow = marineSnow(st * 1.25, t);
col += vec3(0.70, 0.82, 1.0) * snow * 0.78;
// ---------- distant light spores ----------
for (int i = 0; i < 10; i++) {
float fi = float(i);
vec2 center = vec2(
sin(fi * 13.1 + t * 0.11),
cos(fi * 9.4 - t * 0.08)
);
center.x *= 0.72;
center.y = fract(center.y * 0.5 + 0.5 + t * 0.025 + fi * 0.13) * 2.4 - 1.2;
float dd = length(st - center);
vec3 cc = mix(VIOLET, CYAN, hash11(fi * 4.7));
col += cc * exp(-dd * 26.0) * 0.065;
}
// ---------- post processing ----------
col *= INTENSITY;
// Extra emission bloom-like lift.
col += pow(max(col, 0.0), vec3(1.35)) * 0.38;
float vignette = 1.0 - 0.38 * smoothstep(0.15, 1.45, length(st));
col *= vignette;
// Softer tone map to keep stronger glow.
col = col / (0.82 + col);
// Mild gamma lift.
col = pow(col, vec3(1.0 / 0.96));
// Fine film grain.
float grain = (hash11(uv.x * 1234.5 + uv.y * 987.6 + t) - 0.5) * 0.022;
col += grain;
fragColor = TDOutputSwizzle(vec4(col, 1.0));
}