Classic metaballs demo effect.
Log in to post a comment.
precision highp float; uniform float iTime; uniform vec2 iResolution; uniform float iAmplitude; // value=0.3, min=0.1, max=0.5, step=.001 uniform float iWhitePoint; // value=0.88, min=0.88, max=1.5, step=.001 uniform float iPhase; // value=2.8, min=0, max=7, step=.1 uniform float iPhaseSky; // value=2.0, min=0, max=7, step=.1 uniform float albedoMode; // value=0, min=0, max=1, step=1 (solid, plasma) #define AA 2 #define MAX_DIST 1000. #define EPSILON 0.01 #define PI 3.1415 #define m3 mat3(-0.7373, 0.4562, 0.4980, 0, -0.7373, 0.6754, 0.6754, 0.4980, 0.5437) #define NUM_BALLS 10 const vec3 lightDir = normalize(vec3(5,2,10)); vec3 matCol, skyCol; vec4 metaballs[NUM_BALLS]; // // Hash functions by Dave Hoskins: // // https://www.shadertoy.com/view/4djSRW // float hash11(float p) { p = fract(p * .1031); p *= p + 33.33; p *= p + p; return fract(p); } float hash12(vec2 p) { vec3 p3 = fract(vec3(p.xyx) * .1031); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); } float hash13(vec3 p3) { p3 = fract(p3 * .1031); p3 += dot(p3, p3.zyx + 31.32); return fract((p3.x + p3.y) * p3.z); } vec2 hash21(float p) { vec3 p3 = fract(vec3(p) * vec3(.1031, .1030, .0973)); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.xx+p3.yz)*p3.zy); } vec2 hash23(vec3 p3) { p3 = fract(p3 * vec3(.1031, .1030, .0973)); p3 += dot(p3, p3.yzx+33.33); return fract((p3.xx+p3.yz)*p3.zy); } vec3 hash31(float p) { vec3 p3 = fract(vec3(p) * vec3(.1031, .1030, .0973)); p3 += dot(p3, p3.yzx+33.33); return fract((p3.xxy+p3.yzz)*p3.zyx); } // // rotation matrices // mat2 rot2D(float a) { return mat2(cos(a), sin(a), -sin(a), cos(a)); } // // shading // const float T2 = 7.5; float filmic_reinhard_curve (float x) { float q = (T2*T2 + 1.0)*x*x; return q / (q + x + T2*T2); } vec3 filmic_reinhard(vec3 x) { float w = filmic_reinhard_curve(iWhitePoint); return vec3( filmic_reinhard_curve(x.r), filmic_reinhard_curve(x.g), filmic_reinhard_curve(x.b)) / w; } // // Background types // vec3 plasma(vec3 pos) { vec3 p = vec3(pos + iTime * 0.05 + 1.); float a = 1.; vec3 n = vec3(0); for (int i = 0; i <7; i++){ p = m3 * p; vec3 s = sin(p.zxy / a) * a; p += s * 2.; n += s; } return n * 0.25 + 0.5; } // // SDF framework by Inigo Quilez: // // https://www.shadertoy.com/view/Xds3zN // float sminCubic( float a, float b ) { float h = max( 1.-abs(a-b), 0.0 ); return min( a, b ) - h*h*h*(1.0/6.0); } float sdMetaBalls( vec3 pos ) { float dmin = MAX_DIST; for (int i=0; i<NUM_BALLS; i++) { float d = length(metaballs[i].xyz - pos) - metaballs[i].w; dmin = sminCubic(dmin, d); } return dmin; } float map(in vec3 pos) { float metaballs = sdMetaBalls(pos); float l = length(pos.xz); float fr = 2. / sqrt(iAmplitude); float f = pos.y+1.35 + iAmplitude * sin((metaballs - 1.5 * iTime + l * smoothstep(.0, 0.5, l)) * fr ) * (1./(4. + l * l)) ; return sminCubic(metaballs, f); } vec3 calcNormal(in vec3 pos) { const vec2 e = vec2(1., -1.)*EPSILON; return normalize( e.xyy*map(pos + e.xyy) + e.yyx*map(pos + e.yyx) + e.yxy*map(pos + e.yxy) + e.xxx*map(pos + e.xxx)); } vec3 bgCol(vec3 rd) { return mix(skyCol, skyCol*.5, max(0., rd.y)); } float intersect(in vec3 ro, in vec3 rd) { float tmax = rd.y < 0. ? -(ro.y + (1.35 + iAmplitude))/rd.y : MAX_DIST; float t = hash13(rd + iTime) * EPSILON; bool hit = false; for (int i=0; i<64; i++) { float res = map(ro+rd*t); if (abs(res) < max(EPSILON, .0025*t) || t > tmax) { hit = true; break; } t += res; } return hit ? t : tmax; } float calcSoftshadow(in vec3 ro, in vec3 rd, in float mint, in float tmax) { float res = 1.0; float t = mint; float ph = 1e10; for (int i=0; i<16; i++) { float h = map(ro + rd*t); float y = h*h/(2.0*ph); float d = sqrt(h*h-y*y); res = min(res, 10.0*d/max(0.0, t-y)); ph = h; t += h; if (res<0.0001 || t>tmax) break; } res = clamp(res, 0.0, 1.0); return smoothstep(0., 1., res);// res*res*(3.0-2.0*res); } vec3 render(in vec3 ro, in vec3 rd) { float t = intersect(ro, rd); vec3 col; if (t < MAX_DIST) { vec3 p = ro + t * rd; vec3 n = calcNormal(p); vec3 ref = reflect(rd, n); vec3 raf = refract(rd, n, 1.0/1.5); vec3 albedo = albedoMode == 0. ? matCol : plasma(.05*p); float fre = 0.03 + 0.97*pow( max(0.0, 1.0+dot(n, rd)), 5.0 ); float dif = max(0., dot(n, lightDir)); vec3 hal = normalize(lightDir-rd); float sha = mix(calcSoftshadow(p, ref, EPSILON, 4.), 1., clamp(p.y, 0., 1.)); float spc = pow(clamp(dot(n, hal), 0., 1.), 32.) * dif; // sub surface scattering vec3 o = p; float a = 0.; for(float i = 0.1; i < 2.5; i += 0.2) { o += i * raf; a += min(0., map(o)); } float sss = 70./max(0.1, -a); col = albedo * (dif * 2. + .1 + pow(max(0., sss), .25)); // fake reflections col = mix(col, sha * skyCol, fre); col += 50. * spc * fre; // fog col = mix(col, bgCol(rd), clamp(1. - exp(-0.1*t) * 1.2, 0., 1.)); } else { col = bgCol(rd); } float sun = 1.5 * pow(dot(rd, lightDir), 16.); col += sun; col = filmic_reinhard(.25 * col); col = smoothstep(-0.025, 1.0, col); return sqrt(col); } void main() { for( int i=0; i<NUM_BALLS; i++ ) { float h = float(i+1)/float(NUM_BALLS); metaballs[i].xyz = vec3(1.2,1.5,1.2)*sin( 6.2831*hash31(h) + hash31(h*2.39996322972865332 )*iTime ); metaballs[i].y += (.1 + hash11(h*1.61803398875)) * iTime + h * 16.; metaballs[i].y = mod(metaballs[i].y + 4., 16.) - 8.; metaballs[i].w = .8 + 0.4*sin(6.28*hash11(-h)); } matCol = .5 + 0.3*sin( iPhase + vec3(0.0,0.6,1.2) ); matCol *= matCol; skyCol = 1.5 + 0.4*sin( iPhaseSky + vec3(0.0,1.0,2.0) ); skyCol *= skyCol; vec3 tot = vec3(0); vec2 q = (gl_FragCoord.xy * 2. - iResolution.xy) / iResolution.y; for (int m=0; m<AA; m++) { for (int n=0; n<AA; n++) { vec2 o = vec2(float(m), float(n))*(2./float(AA)) - .5; // rotate AA offset to reduce aliasing o *= rot2D(0.463647609); vec2 p = q + o/iResolution.y; vec3 ro = vec3(0., 0.25, -7.); vec3 rd = normalize(vec3(p.xy, 2.5)); vec3 col = render(ro, rd); tot += col; } } tot *= (1./float(AA*AA)); // noise tot += vec3(hash12(gl_FragCoord.xy) * 0.02); gl_FragColor = vec4(tot, 1.); }