AETHER
arrastra el éter · toca para ondular
wogl.io
#version 300 es
in vec2 p; void main(){ gl_Position=vec4(p,0.,1.); }
#version 300 es
precision highp float;
out vec4 frag;
uniform vec2 uRes;
uniform float uTime, uAwaken, uBlink, uDilate;
uniform vec2 uGaze;
uniform int uSrcN, uPokeN, uIrid, uFbmOct;
uniform vec4 uSrc[14]; // xy=centro · z=radio · w=peso (cuerpo + melcocha + estela)
uniform vec3 uPoke[4]; // xy=centro · z=nacimiento
float hash(vec2 p){ p=fract(p*vec2(123.34,456.21)); p+=dot(p,p+45.32); return fract(p.x*p.y); }
float noise(vec2 p){ vec2 i=floor(p),f=fract(p);
float a=hash(i),b=hash(i+vec2(1,0)),c=hash(i+vec2(0,1)),d=hash(i+vec2(1,1));
vec2 u=f*f*(3.0-2.0*f); return mix(mix(a,b,u.x),mix(c,d,u.x),u.y); }
float fbm(vec2 p,int oct){ float v=0.0,a=0.55; for(int i=0;i<4;i++){ if(i>=oct) break; v+=a*noise(p); p=p*2.03+vec2(1.7,9.2); a*=0.5; } return v; }
mat2 rot(float a){ float c=cos(a),s=sin(a); return mat2(c,-s,s,c); }
vec2 asp(vec2 uv){ return (uv*uRes-0.5*uRes)/uRes.y; }
// ── el OJO (vive dentro del cristal; reacciona al picar) ──
float starfield(vec2 p){
vec2 g=floor(p*90.0); float h=hash(g);
float s=step(0.986,h);
vec2 f=fract(p*90.0)-0.5; s*=smoothstep(0.12,0.0,length(f));
return s*(0.4+0.6*sin(uTime*3.0+h*40.0));
}
vec3 drawEye(vec2 p, vec2 ec, float sz, inout float mask){
vec2 ep=(p-ec)/sz; float ex=0.92, ey=1.0;
float eball=length(ep/vec2(ex,ey));
if(eball>1.06){ mask=0.0; return vec3(0.0); }
float aperture=mix(1.0,0.035,clamp(uBlink,0.0,1.0));
float band=abs(ep.y)/ey;
float open=smoothstep(aperture+0.06,aperture-0.06,band);
vec3 sclera=mix(vec3(0.05,0.10,0.22),vec3(0.10,0.20,0.40),0.5+0.5*ep.y);
vec3 col=sclera;
vec2 gz=clamp(uGaze*0.16,-0.38,0.38);
vec2 ip=ep-gz;
float ir=length(ip)/0.60;
float irisM=smoothstep(1.0,0.9,ir);
float fib=fbm(vec2(atan(ip.y,ip.x)*4.0, ir*6.0)+uTime*0.2, uFbmOct);
vec3 iris=mix(vec3(0.05,0.22,0.58),vec3(0.38,0.72,1.0),fib);
iris=mix(iris,vec3(0.70,0.35,1.0),uAwaken*0.55);
col=mix(col,iris,irisM);
float pw=mix(0.10,0.32,clamp(uDilate+uAwaken*0.4,0.0,1.0)), ph=0.62;
float pup=length(ip/vec2(pw,ph));
col=mix(col,vec3(0.0),smoothstep(1.0,0.85,pup));
float hl=smoothstep(0.10,0.0,length(ip-vec2(-0.17,0.21)));
col+=hl*vec3(0.8,0.9,1.0);
vec3 lid=mix(vec3(0.03,0.06,0.16),vec3(0.05,0.10,0.24),0.5+0.5*ep.y);
col=mix(lid,col,open);
float edge=smoothstep(1.06,1.0,eball)-smoothstep(1.0,0.94,eball);
col=mix(col,vec3(0.0),edge*0.7);
mask=smoothstep(1.03,0.98,eball);
return col;
}
// ── fondo abisal que el cristal refracta (con el ojo dentro) ──
vec3 bgCol(vec2 uv, bool eyeOn){
vec2 p=asp(uv);
// éter cósmico · índigo profundo + nebulosa + estrellas
vec3 c=mix(vec3(0.03,0.04,0.11), vec3(0.006,0.010,0.035), uv.y);
float n1=fbm(p*1.5+vec2(uTime*0.02,0.0), uFbmOct);
float n2=fbm(p*3.1-vec2(0.0,uTime*0.015)+n1, uFbmOct);
c+=mix(vec3(0.10,0.16,0.45), vec3(0.34,0.14,0.52), n2)*pow(n2,2.2)*0.55;
c+=starfield(p)*vec3(0.8,0.9,1.0);
c+=vec3(0.10,0.28,0.75)*exp(-dot(p,p)*2.2)*(0.5+0.5*uAwaken);
if(eyeOn){ float em=0.0; vec3 e=drawEye(p, vec2(0.0,0.0), 0.17, em); c=mix(c,e,em); }
return c;
}
vec3 wShoulder(vec3 c){
c = max(c, 0.0);
float p = max(c.r, max(c.g, c.b));
if (p <= 0.70) return c;
float s = 0.70 + 0.30 * (1.0 - exp(-(p - 0.70) / 0.30));
return c * (s / p);
}
void main(){
vec2 uv=gl_FragCoord.xy/uRes;
vec2 p=asp(uv);
float t=uTime, aw=clamp(uAwaken,0.0,1.0);
// ── campo de metaballs (melcocha) + gradiente analítico (nítido) ──
float f=0.0; vec2 g=vec2(0.0);
for(int i=0;i<14;i++){ if(i>=uSrcN) break;
vec4 S=uSrc[i]; vec2 d=p-S.xy; float q=dot(d,d)+0.0009;
float r2=S.z*S.z*S.w; f+=r2/q; g+=-2.0*r2*d/(q*q);
}
// ── ondas al picar (ripples en el cristal) ──
for(int i=0;i<4;i++){ if(i>=uPokeN) break;
vec2 d=p-uPoke[i].xy; float dist=length(d)+1e-4; float age=t-uPoke[i].z;
float w=sin(dist*32.0-age*14.0)*exp(-dist*4.2)*exp(-age*2.1);
g+=(d/dist)*w*1.7;
}
float inside=smoothstep(0.85,1.15,f);
// normal pseudo-3D del cristal
vec3 N=normalize(vec3(-g*0.035, 1.0));
// ── refracción: mira el fondo desplazado por la normal ──
vec2 disp=N.xy*0.075*inside;
vec3 through=bgCol(uv+disp, true);
vec3 base=bgCol(uv, false); // fuera del cristal (sin ojo)
// ── vidrio: tinte, fresnel, iridiscencia, especular nítido ──
vec3 glass=through;
glass=mix(glass, glass*vec3(0.45,0.62,1.0)+vec3(0.02,0.05,0.14), 0.5); // tinte azul etéreo
float fres=pow(1.0-clamp(N.z,0.0,1.0),3.0);
glass+=fres*vec3(0.40,0.70,1.0)*0.6; // borde luminoso cyan
if(uIrid==1){
vec3 irid=0.5+0.5*cos(6.2831*(vec3(N.x,N.y,N.x)*1.6+vec3(0.0,0.33,0.66))+aw);
glass=mix(glass, glass*0.5+irid*0.6, fres*0.55); // arcoíris de cristal
}
vec3 L1=normalize(vec3(-0.42,0.55,0.72));
vec3 L2=normalize(vec3(0.5,0.35,0.6));
float spec=pow(clamp(dot(N,L1),0.0,1.0),70.0)+0.6*pow(clamp(dot(N,L2),0.0,1.0),120.0);
glass+=spec*vec3(1.0,1.0,0.92); // brillos de vidrio
vec3 col=mix(base, glass, inside);
// ── acabado nítido: pulso al despertar + viñeta ──
col*=1.0+0.08*aw*sin(t*6.0);
col*=mix(0.45,1.0, smoothstep(1.4,0.2,length(p)));
col=max(col,0.0);{float L=dot(col,vec3(0.2126,0.7152,0.0722));float o=max(L-0.70,0.0);float Lt=L-o+o/(1.0+o);col*= L>0.0 ? Lt/L : 0.0;}col=pow(clamp(col,0.0,1.0),vec3(0.4545));{float L=dot(col,vec3(0.2126,0.7152,0.0722));col=clamp(mix(vec3(L),col,1.3),0.0,1.0);}
frag=vec4(wShoulder(col),1.0);
}