Back to shaders
Shader test bench
20250919
runnable fragment
Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.
Code
precision mediump float;
// TouchDesigner GLSL TOP - Pixel Shader
// Enhanced raymarch scene with flowing geometries and ethereal lighting
#define fragColor gl_FragColor
uniform float u_time;
uniform vec2 u_resolution;
// ------------------------------
// Enhanced Quality Settings
// ------------------------------
const int MAX_STEPS = 64;
const float MAX_DIST = 120.0;
const float SURF_DIST = 0.0005;
const float FOV = 1.8;
// ------------------------------
// Your palette
// ------------------------------
const int PALETTE_COUNT = 10;
const vec3 PALETTE[PALETTE_COUNT] = vec3[PALETTE_COUNT](
vec3(0.3568627, 0.5450980, 0.8745098), // #5b8bdf
vec3(0.2431373, 0.4745098, 0.7803922), // #3e79c7
vec3(0.0313725, 0.3450980, 0.3098039), // #08584f
vec3(0.9176471, 0.8274510, 1.0000000), // #ead3ff
vec3(0.0627451, 0.1647059, 0.4039216), // #102a67
vec3(0.2352941, 0.4901961, 0.8196078), // #3c7dd1
vec3(0.3960784, 0.3372549, 0.7411765), // #6556bd
vec3(0.3921569, 0.3686275, 0.7450980), // #645ebe
vec3(0.0823529, 0.1764706, 0.5686275), // #152d91
vec3(0.5411765, 0.6470588, 0.9647059) // #8aa5f6
);
mat2 rot(float a){
float s = sin(a), c = cos(a);
return mat2(c, -s, s, c);
}
mat3 rotX(float a){
float s = sin(a), c = cos(a);
return mat3(1,0,0, 0,c,-s, 0,s,c);
}
mat3 rotY(float a){
float s = sin(a), c = cos(a);
return mat3(c,0,s, 0,1,0, -s,0,c);
}
mat3 rotZ(float a){
float s = sin(a), c = cos(a);
return mat3(c,-s,0, s,c,0, 0,0,1);
}
vec3 palette(float t){
t = clamp(t, 0.0, 0.9999);
float scaled = t * float(PALETTE_COUNT - 1);
int idx = int(floor(scaled));
int nextIdx = min(idx + 1, PALETTE_COUNT - 1);
float f = fract(scaled);
return mix(PALETTE[idx], PALETTE[nextIdx], smoothstep(0.0, 1.0, f));
}
float hash12(vec2 p){
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
float hash13(vec3 p){
p = fract(p * 0.1031);
p += dot(p, p.zyx + 31.32);
return fract((p.x + p.y) * p.z);
}
vec3 hash33(vec3 p){
p = fract(p * vec3(.1031, .1030, .0973));
p += dot(p, p.yxz+33.33);
return fract((p.xxy + p.yxx)*p.zyx);
}
vec3 toneMapFilmic(vec3 x){
x = max(vec3(0.0), x - 0.004);
return (x*(6.2*x + 0.5)) / (x*(6.2*x + 1.7) + 0.06);
}
float sdSphere(vec3 p, float r){
return length(p) - r;
}
float sdTorus(vec3 p, vec2 t){
vec2 q = vec2(length(p.xz) - t.x, p.y);
return length(q) - t.y;
}
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 sdOctahedron(vec3 p, float s){
p = abs(p);
return (p.x+p.y+p.z-s)*0.57735027;
}
float displace(vec3 p, float t){
return sin(p.x*2.0 + t)*sin(p.y*2.0 + t)*sin(p.z*2.0 + t)*0.1;
}
float smin(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);
}
float mapScene(vec3 p, out vec3 albedo, out float glow){
float t = u_time * 0.3;
vec3 q = p;
q = rotY(t*0.4 + sin(t*0.2)*0.5) * q;
q = rotX(sin(t*0.3)*0.3) * q;
q.xy = rot(0.3*sin(t*0.8) + 0.4*sin(q.z*0.5 + t)) * q.xy;
vec3 q1 = q + vec3(sin(t*0.7)*1.2, 0.8*sin(t*0.9 + 1.0), cos(t*0.6)*0.8);
float sphere1 = sdSphere(q1, 1.0 + 0.2*sin(t*1.1 + q1.x*0.5) + 0.1*cos(t*1.7));
vec3 q2 = rotZ(t*0.5) * q;
float pulse = 0.5 + 0.5*sin(t*2.0);
float torus = sdTorus(q2, vec2(1.4 + 0.4*sin(t*0.6), 0.3 + 0.1*pulse));
vec3 orbit1 = vec3(2.5*sin(t*0.8), 1.5*cos(t*0.5), 2.0*sin(t*0.6 + 1.57));
vec3 q3 = rotX(t*0.7) * rotY(t*0.9) * (q - orbit1);
float sphere2 = sdSphere(q3, 0.6 + 0.1*sin(t*2.3));
vec3 orbit2 = vec3(cos(t*0.4)*2.0, sin(t*0.3)*1.0, sin(t*0.4)*2.0);
vec3 q4 = rotY(t*1.2) * (q - orbit2);
float box = sdBox(q4, vec3(0.7, 0.3 + 0.2*abs(sin(t*1.5)), 0.7));
vec3 orbit3 = vec3(0.0, 2.0*sin(t*0.4), 0.0);
vec3 q5 = rotY(t*1.1) * rotX(t*0.8) * (q - orbit3);
float octa = sdOctahedron(q5, 0.5 + 0.15*sin(t*1.8));
vec3 orbit4 = vec3(1.8*cos(t*0.9 + 3.14), 0.5, 1.8*sin(t*0.9 + 3.14));
vec3 q6 = rotZ(t*1.3) * (q - orbit4);
float sphere3 = sdSphere(q6, 0.4 + 0.1*cos(t*2.7));
float blend1 = 0.4 + 0.2*sin(t*0.7);
float blend2 = 0.3 + 0.15*cos(t*0.9);
float blend3 = 0.5 + 0.2*sin(t*1.1);
float d1 = smin(sphere1, torus, blend1);
float d2 = smin(d1, sphere2, blend2);
float d3 = smin(d2, box, 0.4);
float d4 = smin(d3, octa, blend3);
float d = smin(d4, sphere3, 0.35);
float colorShift = t*0.1 + length(p)*0.03;
float key = 0.3 + 0.4*sin(colorShift + p.x*0.8) + 0.3*sin(p.y*1.1 - t*0.7) + 0.2*cos(p.z*0.9 + t*0.5);
albedo = palette(fract(key));
float energy = 0.0;
energy += exp(-abs(sphere1 - torus)*3.0) * (0.4 + 0.3*sin(t*2.1));
energy += exp(-abs(sphere2 - octa)*4.0) * (0.3 + 0.2*cos(t*1.9));
energy += exp(-abs(box - sphere3)*5.0) * (0.2 + 0.2*sin(t*2.5));
energy += exp(-abs(torus - sphere3)*6.0) * (0.3 + 0.2*cos(t*1.6));
glow = 0.2 + energy + 0.1*sin(t*3.0 + length(p));
return d;
}
float mapDist(vec3 p){
vec3 c; float g;
return mapScene(p, c, g);
}
vec3 calcNormal(vec3 p){
const vec2 e = vec2(1e-3, 0.0);
return normalize(vec3(
mapDist(p + vec3(e.x, e.y, e.y)) - mapDist(p - vec3(e.x, e.y, e.y)),
mapDist(p + vec3(e.y, e.x, e.y)) - mapDist(p - vec3(e.y, e.x, e.y)),
mapDist(p + vec3(e.y, e.y, e.x)) - mapDist(p - vec3(e.y, e.y, e.x))
));
}
vec3 postProcess(vec3 color, vec2 uv){
float r = length(uv);
float vig = 1.0 - smoothstep(0.3, 1.2, r);
color *= mix(0.7, 1.1, vig);
vec2 chromaOffset = normalize(uv) * r * r * 0.003;
float chromaR = length(color);
float chromaG = length(color * vec3(0.7, 1.0, 0.7));
float chromaB = length(color * vec3(0.7, 0.7, 1.0));
color.r = mix(color.r, chromaR, abs(chromaOffset.x));
color.g = mix(color.g, chromaG, abs(chromaOffset.y) * 0.5);
color.b = mix(color.b, chromaB, abs(chromaOffset.x) * 0.7);
float grain = (hash12(uv * u_resolution + vec2(u_time*0.11, u_time*0.07)) - 0.5) * 0.008;
color += grain;
color *= 1.0 + 0.05*sin(uv.y * u_resolution.y * 2.0);
color = toneMapFilmic(color * 1.2);
return clamp(color, 0.0, 1.0);
}
vec3 camRay(vec3 ro, vec3 ta, vec2 uv){
vec3 fw = normalize(ta - ro);
vec3 rt = normalize(cross(vec3(0.0,1.0,0.0), fw));
vec3 up = cross(fw, rt);
return normalize(uv.x * rt * FOV + uv.y * up * FOV + fw);
}
vec3 background(vec3 rd, float t){
float h = clamp(0.5 + 0.5*rd.y, 0.0, 1.0);
float r = length(rd.xz);
vec3 stars = vec3(0.0);
for(int i = 0; i < 3; i++){
float layer = 20.0 + float(i)*15.0;
vec2 grid = floor(rd.xz * layer);
vec2 id = grid + vec2(float(i)*13.7, float(i)*7.3);
float star = hash12(id);
if(star > 0.995){
vec2 gv = fract(rd.xz * layer) - 0.5;
float d = length(gv);
float twinkle = 0.7 + 0.3*sin(t*3.0 + star*31.4 + float(i)*2.1);
float brightness = smoothstep(0.15, 0.0, d) * (star - 0.995) * 100.0 * twinkle;
vec3 starColor = palette(fract(star*4.7 + t*0.05 + float(i)*0.33));
stars += brightness * starColor;
}
}
float gradientShift = t*0.03 + sin(t*0.08)*0.5;
float waveA = 0.12 + 0.25*sin(gradientShift);
float waveB = 0.62 + 0.30*sin(gradientShift + 3.14 + rd.x*2.0);
vec3 a = palette(fract(waveA));
vec3 b = palette(fract(waveB));
vec3 c = palette(fract(waveA + 0.33));
float blend1 = smoothstep(0.0, 0.7, h);
float blend2 = smoothstep(0.3, 1.0, h);
float radialMod = 0.5 + 0.5*sin(r*3.0 + t*0.2);
vec3 gradient = mix(a, b, blend1);
gradient = mix(gradient, c, blend2 * 0.3);
gradient *= 0.8 + 0.4*radialMod;
float aurora = abs(sin(rd.y*8.0 + rd.x*5.0 + t*0.7)) *
abs(cos(rd.y*6.0 - rd.z*4.0 + t*0.5));
aurora = pow(aurora, 3.0) * 0.3;
vec3 auroraColor = palette(fract(0.8 + t*0.08 + aurora*2.0));
return gradient * 0.7 + stars + aurora * auroraColor;
}
vec3 render(vec2 fragCoord){
vec2 uv = (fragCoord - 0.5 * u_resolution) / u_resolution.y;
float t = u_time * 0.3;
float rad = 4.5 + 0.8*sin(t*0.5) + 0.3*cos(t*0.3);
float height = 2.0 + 1.2*sin(t*0.4) + 0.5*cos(t*0.8);
float camOrbit = t*0.25 + 0.3*sin(t*0.15);
vec3 ro = vec3(rad * sin(camOrbit), height, rad * cos(camOrbit));
vec3 ta = vec3(0.5*sin(t*0.3), 0.3*sin(t*0.7), 0.5*cos(t*0.4));
vec3 rd = camRay(ro, ta, uv);
float travel = 0.0;
for(int i = 0; i < MAX_STEPS; ++i){
vec3 pos = ro + rd * travel;
vec3 albedo;
float glow;
float d = mapScene(pos, albedo, glow);
if(d < SURF_DIST){
vec3 n = calcNormal(pos);
vec3 lp1 = vec3(3.0*sin(t*1.2 + 0.5), 2.5 + 1.5*sin(t*0.8), 3.0*cos(t*1.2 + 0.5));
vec3 lp2 = vec3(-2.0*cos(t*0.9 + 2.1), 2.0*sin(t*0.6), 2.5*sin(t*0.9 + 2.1));
vec3 lp3 = vec3(2.2*sin(t*0.7 + 4.2), -1.5 + 0.8*cos(t*0.5), 2.2*cos(t*0.7 + 4.2));
vec3 l1 = normalize(lp1 - pos);
vec3 l2 = normalize(lp2 - pos);
vec3 l3 = normalize(lp3 - pos);
float diff1 = max(dot(n, l1), 0.0);
float diff2 = max(dot(n, l2), 0.0) * 0.6;
float diff3 = max(dot(n, l3), 0.0) * 0.4;
float rim = pow(1.0 - max(dot(n, -rd), 0.0), 1.8);
float fresnel = pow(1.0 - max(dot(n, -rd), 0.0), 3.0);
vec3 ltCol1 = palette(fract(0.08 + t*0.1 + sin(t*1.5)*0.1));
vec3 ltCol2 = palette(fract(0.45 + t*0.08 + cos(t*1.2)*0.1));
vec3 ltCol3 = palette(fract(0.78 + t*0.12 + sin(t*0.9)*0.1));
vec3 col = vec3(0.0);
col += albedo * (0.15 + 0.8 * diff1);
col += albedo * ltCol2 * diff2 * 0.4;
col += albedo * ltCol3 * diff3 * 0.3;
col += ltCol1 * rim * 0.5;
col += albedo * glow * (0.3 + 0.4*sin(t*2.0 + pos.x*3.0));
col += fresnel * palette(fract(glow + t*0.1)) * 0.2;
float fog = exp(-travel * 0.08);
vec3 fogColor = palette(fract(0.9 + t*0.02)) * 0.1;
col = mix(fogColor, col, fog);
return postProcess(col, uv);
}
travel += d;
if(travel > MAX_DIST) break;
}
vec3 bg = background(rd, t);
return postProcess(bg, uv);
}
void main(){
vec3 color = render(gl_FragCoord.xy);
color = pow(clamp(color, 0.0, 1.0), vec3(0.45454545));
fragColor = vec4(color, 1.0);
}