Yeah, I think if I just slow down the timer a little, it would make a great main menu. And..it seems as the Youtube just hadn't finished processing the higher-res versions of the video, as they're available now.
Anyway, the easily reusable stud routines are here:
Code:
const float studRad = 0.3;
const float studHeight = 0.2;
const float studBorder = 0.03;
float yGrid = iResolution.y / 20.;
const vec2 halfXY = vec2(0.5, 0.5);
vec2 gridRes = vec2(iResolution.x/iResolution.y * yGrid,yGrid);
vec2 scaledUv;
vec2 gridC;
// Photoshop/GIMP style multiplicative-ish style filter.
float blend(float a, float b){
return (a < 0.5) ? 2.*a*b : (1. - 2.*(1.-a)*(1.-b));
}
vec4 blend(vec4 a, vec4 b){
return vec4(blend(a.x, b.x),
blend(a.y, b.y),
blend(a.z, b.z),
1);
}
// Chunks up the screen into blocks.
vec2 baseXY(vec2 uv) {
scaledUv = uv*gridRes;
gridC = floor(scaledUv);
return (gridC / gridRes);
}
// Essentially convolves a virtual pixel with the stud pattern.
vec4 brickify(vec4 baseColor) {
vec2 subGrid = scaledUv - gridC - halfXY;
float rad = length(subGrid);
float lightFactor = smoothstep(-studRad, studRad, subGrid.y);
float pixelsPerGrid = iResolution.x / gridRes.x;
vec4 borderColor = vec4(lightFactor, lightFactor, lightFactor, (abs(rad - (studRad - 0.5*studBorder)) <= 0.5*studBorder) ? 0.5*clamp(pixelsPerGrid*(0.5 * studBorder - abs(rad - (studRad - 0.5*studBorder))), 0., 1.) : 0.);
float rightFactor = 0.3;// mix(0.5, 0, (subGrid.y + 0.5));
vec4 rightColor = vec4(rightFactor, rightFactor, rightFactor, (0.5 - subGrid.x) <= studBorder ? 0.3 : 0.);
float bottomFactor = 0.3;
vec4 bottomColor = vec4(bottomFactor, bottomFactor, bottomFactor, (0.5 + subGrid.y) <= studBorder ? 0.3 : 0.);
vec4 fragColor = vec4(0.5,0.5,0.5,1);//baseColor;
fragColor = mix(fragColor, borderColor, borderColor.w);
if(abs(subGrid.x) <= studRad - 1./pixelsPerGrid && subGrid.y <= 0.){
float angle = acos(subGrid.x / studRad);
float yInt = -sin(angle) * studRad;
float vFac = 0.5*smoothstep(0., studHeight, (yInt - subGrid.y) * 1.5*exp(-pow(subGrid.x,2.)));
float sFac = vFac;
vec4 shadowColor = vec4(sFac, sFac, sFac, subGrid.y <= yInt ? 1. : clamp(1. - pixelsPerGrid*abs(rad - studRad), 0., 1.));
fragColor = mix(fragColor, shadowColor, 0.5*shadowColor.w);
}
fragColor = mix(fragColor, rightColor, rightColor.w);
fragColor = mix(fragColor, bottomColor, bottomColor.w);
fragColor = blend(baseColor, fragColor);
return fragColor;
}
The use is pretty straightforward. There a few parameters at the top governing the appearance of each block. iResolution is a uniform vec2 containing the real pixel size of the screen. Remap your UV coordinates through baseXY to get virtual pixels, which also sets up a few parameters used by brickify, which you call at the end with your output color.
The plasma code basically just makes moving swirly bits:
Code: float time = iGlobalTime * 5.;
uv = uv - halfXY;
uv += vec2(sin(time / 47.), cos(time / 37.));
float theta = atan2(uv.x, uv.y);
float r = length(uv);
vec2 rg = vec2(0.5 + 0.5*cos(theta+time / 37. + sin(7.*r + time / 5.)), 0.5 + 0.5*sin(theta+time / 13. + cos(11.*r + time / 17.)));
vec2 uvN = sqrt(abs(uv - rg));
float thetaN = atan2(uvN.y, uvN.x);
float rN = length(uvN);
rg = rg*halfXY + halfXY * vec2(rg.x + pow(cos(thetaN + sin(rN + time / 5.) + time / 43.),2.), rg.y + pow(sin(thetaN + cos(rN + time / 11.) + time / 31.),2.));
rg *= vec2(abs(sin(rg.x * 17. / 5.)), abs(cos(rg.y * 23. / 3.)));
float thetaR = atan2(rg.x, rg.y);
float rgM = length(rg);
rg = halfXY * (rg + (halfXY+halfXY*vec2(sin(thetaR * rgM)*cos(thetaR * rgM),cos(thetaR + time / 47.)*sin(rgM))));
rg = vec2(mix(sqrt(rg.x),rg.x*rg.x,clamp(rg.y - rg.x,0.,1.)),mix(sqrt(rg.y),rg.y*rg.y,clamp(rg.x - rg.y,0.,1.)));
vec4 baseColor = vec4(rg * rg, 0.2,1.0);
And relies on this utility routine:
Code: const float pi = 3.1415926535897;
float atan2(in float y, in float x)
{
bool s = (abs(x) > abs(y));
return mix(pi/2.0 - atan(x,y), atan(y,x), s ? 1. : 0.);
}