<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>基于 WebGL 的着色器时钟</title>
<style>
html {
height: 100%;
}
body {
background: #333;
overflow: hidden;
padding: 0;
margin: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
}
canvas {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script id="vertexShader" type="x-shader/x-vertex">
#version 300 es
precision highp float;
in vec4 position;
void main() {
gl_Position = vec4( position );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
#version 300 es
#if __VERSION__ < 130
#define TEXTURE2D texture2D
#else
#define TEXTURE2D texture
#endif
precision highp float;
out vec4 fragColor;
uniform vec2 u_resolution;
uniform vec4 u_mouse;
uniform float u_time;
uniform float u_date;
#define R u_resolution
#define T u_time
#define M u_mouse
#define PI 3.14159265359
#define PI2 6.28318530718
#define MIN_DIST 1e-4
#define MAX_DIST 50.
float box( in vec2 p, in vec2 b ){
vec2 d = abs(p)-b;
return length(max(d,0.))+min(max(d.x,d.y),0.);
}
float rbox( in vec2 p, in vec2 b, in vec4 r ) {
r.xy = (p.x>0.)?r.xy : r.zw;
r.x = (p.y>0.)?r.x : r.y;
vec2 q = abs(p)-b+r.x;
return min(max(q.x,q.y),0.)+length(max(q,0.))-r.x;
}
const vec4 bz = vec4(.075,.055,.035,.0);
const vec4 bo = vec4(.175,.15,.075,.025);
const vec2 oa = vec2(.15,.1);
float get0(vec2 p) {
float bt = max(
rbox(p,vec2(.15,.175),bz.xxxx),
-rbox(p,vec2(.1,.125),bz.zzzz)
);
return bt;
}
float get1(vec2 p) {
float bt = box(p,vec2(.025,.175));
return bt;
}
float get2(vec2 p) {
float bt = max(
rbox(p-vec2(.0,.075),oa,bz.xxww),
-rbox(p-vec2(-.075,.075),vec2(.175,.05),bz.zzww)
);
bt = max(bt,-box(p-vec2(-.085,.045),vec2(.08,.08)));
float bb = max(
rbox(p+vec2(.0,.075),oa,bz.wwxx),
-rbox(p+vec2(-.075,.075),vec2(.175,.05),bz.wwzz)
);
bb = max(bb,-box(p+vec2(-.085,.045),vec2(.08,.08)));
return min(bt,bb);
}
float get3(vec2 p) {
vec2 of = vec2(0,.075);
float bt = max(
rbox(p-of,oa,bz.xxww),
-rbox(p-of+vec2(.05,0),vec2(.15,.05),bz.zzww)
);
float bb = max(
rbox(p+of,oa,bz.xxww),
-rbox(p+of+vec2(.05,0),vec2(.15,.05),bz.zzww)
);
return min(bt,bb);
}
float get4(vec2 p) {
float bt = min(box(vec2(p.x-.125,p.y),vec2(.025,.175)),
rbox(p-vec2(.0,.075),oa,bz.wwwx)
);
bt = max(
bt,
-rbox(p-vec2(0,.115),vec2(.1,.095),bz.wwwz)
);
return bt;
}
float get5(vec2 p) {
float bt = max(
rbox(p-vec2(.0,.075),oa,bz.wwwx),
-rbox(p-vec2(.075,.075),vec2(.175,.05),bz.wwwz)
);
bt = max(bt,-box(p-vec2(.085,.045),vec2(.08,.08)));
float bb = max(
rbox(p+vec2(.0,.075),oa,bz.xxww),
-rbox(p+vec2(.075,.075),vec2(.175,.05),bz.zzww)
);
bb = max(bb,-box(p+vec2(.085,.045),vec2(.08,.08)));
return min(bt,bb);
}
float get6(vec2 p) {
float bt = max(
rbox(p-vec2(.0,.075),oa,bz.wwxw),
-rbox(p-vec2(.075,.075),vec2(.175,.05),bz.wwzw)
);
bt = max(bt,-box(p-vec2(.085,.045),vec2(.08,.08)));
float bb = max(
rbox(p+vec2(.0,.075),oa,bz.xxwx),
-rbox(p+vec2(.0,.075),vec2(.1,.05),bz.zzwz)
);
return min(bt,bb);
}
float get7(vec2 p) {
float bt = max(
rbox(p+vec2(0,.0),vec2(.15,.175),bz.xwww),
-rbox(p+vec2(.05,.045),vec2(.15,.175),bz.zwww)
);
return bt;
}
float get8(vec2 p) {
float bt = max(
rbox(p-vec2(.0,.075),oa,bz.xxxx),
-rbox(p-vec2(.0,.075),vec2(.1,.05),bz.zzzz)
);
float bb = max(
rbox(p+vec2(.0,.075),oa,bz.xxxx),
-rbox(p+vec2(.0,.075),vec2(.1,.05),bz.zzzz)
);
return min(bt,bb);
}
float get9(vec2 p) {
float bt = max(
rbox(p-vec2(.0,.075),oa,bz.xwxx),
-rbox(p-vec2(.0,.075),vec2(.1,.05),bz.zwzz)
);
float bb = max(
rbox(p+vec2(.0,.075),oa,bz.wxxw),
-rbox(p+vec2(.075,-.02),vec2(.175,.15),bz.zzwz)
);
return min(bt,bb);
}
float hash21(vec2 p) {return fract(sin(dot(p, vec2(27.69, 32.53)))*437.53);}
mat2 rot(float a){ return mat2(cos(a),sin(a),-sin(a),cos(a)); }
float lerp (float b, float e, float t) { return clamp((t - b) / (e - b), 0., 1.); }
float eic (float t) { return t*t*t; }
float opx(in float d, in float z, in float h){
vec2 w = vec2( d, abs(z) - h );
return min(max(w.x, w.y), 0.) + length(max(w, 0.));
}
float h1,h2,m1,m2,s1,s2,tmod;
vec3 hp,hit;
vec2 map(vec3 p) {
vec2 res = vec2(1e5,0);
vec3 q = p + vec3(2,0,0);
float ofs = 2.8;
float mn = -.62831853071;
float sz = 1.3;
vec2 q1 = q.yz;
vec2 q2 = q.yz;
q1*=rot(ofs+(h1*mn));
q2*=rot(ofs+(h2*mn));
float d = opx(abs(length(q1)-sz)-.01,q.x,.25)-.025;
if(d<res.x) {
res = vec2(d,1);
hit = vec3(q.x,q1.y,-q1.x);
}
float e = opx(abs(length(q2)-sz)-.01,q.x-.75,.25)-.025;
if(e<res.x) {
res = vec2(e,2);
hit = vec3(q.x,q2.y,-q2.x);
}
q = p + vec3(0,0,0);
q1 = p.yz;
q2 = p.yz;
q1*=rot(ofs+(m1*mn));
q2*=rot(ofs+(m2*mn));
float g = opx(abs(length(q1)-sz)-.01,q.x,.25)-.025;
if(g<res.x) {
res = vec2(g,3);
hit = vec3(q.x,q1.y,-q1.x);
}
float h = opx(abs(length(q2)-sz)-.01,q.x-.75,.25)-.025;
if(h<res.x) {
res = vec2(h,4);
hit = vec3(q.x,q2.y,-q2.x);
}
q = p - vec3(2,0,0);
q1 = p.yz;
q2 = p.yz;
q1*=rot(ofs+(s1*mn));
q2*=rot(ofs+(s2*mn));
float j = opx(abs(length(q1)-sz)-.01,q.x,.25)-.025;
if(j<res.x) {
res = vec2(j,5);
hit = vec3(q.x,q1.y,-q1.x);
}
float k = opx(abs(length(q2)-sz)-.01,q.x-.75,.25)-.025;
if(k<res.x) {
res = vec2(k,6);
hit = vec3(q.x,q2.y,-q2.x);
}
float fl = length(p.zy)-.75;
if(fl<res.x) {
res = vec2(fl,10);
hit = p;
}
return res;
}
vec3 normal(vec3 p, float t) {
t*=MIN_DIST;
float d = map(p).x;
vec2 e = vec2(t,0);
vec3 n = d - vec3(
map(p-e.xyy).x,
map(p-e.yxy).x,
map(p-e.yyx).x
);
return normalize(n);
}
vec2 marcher(vec3 ro, vec3 rd, inout vec3 p, int steps) {
float d=0.,m=0.;
for(int i=0;i<steps;i++){
vec2 t = map(p);
d += t.x;
m = t.y;
p = ro + rd * d;
if(abs(t.x)<d*MIN_DIST||d>75.) break;
}
return vec2(d,m);
}
float getDigits(vec2 nv,int dec) {
float d = 1e5;
if(dec == 0) d = get0(nv);
if(dec == 1) d = get1(nv);
if(dec == 2) d = get2(nv);
if(dec == 3) d = get3(nv);
if(dec == 4) d = get4(nv);
if(dec == 5) d = get5(nv);
if(dec == 6) d = get6(nv);
if(dec == 7) d = get7(nv);
if(dec == 8) d = get8(nv);
if(dec == 9) d = get9(nv);
return d;
}
void main( )
{
vec2 F = gl_FragCoord.xy;
float idate = u_date;
int sec = int(mod(idate,60.));
int minute = int(mod(idate/60.,60.));
int hour = int(mod(idate/3600.,12.));
int ampm = int(mod(idate/3600.,24.));
float num = float(hour);if(num == 0.) num = 12.;
h1 = floor(mod(num / pow(10.0,1.),10.0));
h2 = floor(mod(num / pow(10.0,0.),10.0));
num = float(minute);
m1 = floor(mod(num / pow(10.0,1.),10.0));
m2 = floor(mod(num / pow(10.0,0.),10.0));
num = float(sec);
s1 = floor(mod(num / pow(10.0,1.),10.0));
s2 = floor(mod(num / pow(10.0,0.),10.0));
float t2 = lerp(0.,1.,mod(idate,1.));
s2 = s2+eic(t2);
float t1 = lerp(9.,10.,mod(idate,10.));
s1 = s1+eic(t1);
float t4 = lerp(59.,60.,mod(idate,60.));
m2 = m2+eic(t4);
float t3 = lerp(599.,600.,mod(idate,600.));
m1 = m1+eic(t3);
float t6 = lerp(3599.,3600.,mod(idate,3600.));
h2 = h2+eic(t6);
float t5 = lerp(3599.,3600.,mod(idate,3600.));
if(hour == 0) hour = 12;
if(hour == 9||hour>11) {
h1 = h1+eic(t5);
}
vec2 uv = (2.* F.xy-R.xy)/max(R.x,R.y);
vec3 ro = vec3(.4,0,R.y<400.?6.:4.75);
vec3 rd = normalize(vec3(uv, -1.0));
float x = M.xy==vec2(0) ? 0. : (M.y/R.y*.2-.1)*PI;
float y = M.xy==vec2(0) ? 0. : (M.x/R.x*.2-.1)*PI;
mat2 rx = rot(x+.2*cos(T*.325)), ry = rot(y+.15*sin(T*.25));
ro.zy*=rx, ro.xz*=ry;
rd.zy*=rx, rd.xz*=ry;
vec3 C = vec3(0), p = ro;
vec2 ray = marcher(ro,rd,p,100);
float d = ray.x, m = ray.y;
hp = hit;
if(d<MAX_DIST) {
vec3 n = normal(p,d);
vec3 lpos = vec3(5,11,12);
vec3 l = normalize(lpos-p);
float diff = clamp(dot(n,l),.1,1.);
float shdw = 1.;
for( float t=.01;t<12.; ) {
float h = map(p + l*t).x;
if( h<MIN_DIST ) { shdw = 0.; break; }
shdw = min(shdw, 32.*h/t);
t += h * .95;
if( shdw<MIN_DIST || t>42. ) break;
}
diff = mix(diff,diff*shdw,.75);
float spec = .75 * pow(max(dot(normalize(p-ro),reflect(normalize(lpos),n)),0.),24.);
vec3 h = ampm<12?vec3(1,.635,0):ampm>19?vec3(.337,.082,.718):vec3(.8);
if(m>0.) {
vec2 uv = vec2(atan(hp.z,hp.y)/PI2,p.x);
vec2 id = vec2(floor(uv.x*10.),1.);
id.x = mod(abs(id.x+5.),10.);
uv.x = fract(uv.x*10.)-.5;
if(m==1.) {uv.y+=2.;id.x=mod(abs(id.x+6.),2.);}
if(m==2.) uv.y+=1.25;
if(m==3.) id.x=mod(abs(id.x+6.),6.);
if(m==4.) uv.y-=.75;
if(m==5.){uv.y-=2.;id.x=mod(abs(id.x+5.)+1.,6.);}
if(m==6.) uv.y-=2.75;
float px = 2./R.x;
vec2 nv = vec2(uv.y,-uv.x)*1.25;
float d = getDigits(nv,int(id.x));
float c = length(uv)-.235;
c=max(c,-d);
vec3 clr = ampm<12?vec3(1.,.35,0):ampm>19?vec3(.68,.14,.98):vec3(.1,.9,.3);
vec3 clx = ampm<12?vec3(1.):ampm>19?vec3(0.6):vec3(.3);
vec3 cld = ampm<12?vec3(.7,.3,.0):ampm>19?vec3(0.1):vec3(.6);
h = mix(h,clx,smoothstep(px,-px,c));
h = mix(h,clr,smoothstep(px,-px,d));
c = length(vec2(abs(uv.x)-.5,uv.y))-.175;
h = mix(h,cld,smoothstep(px,-px,c));
}
if(m==10.) {
vec3 clr = ampm<12?vec3(.973,.89,.05):ampm>19?vec3(.235,.165,.75):vec3(.72,.93,.52);
vec3 cld = ampm<12?vec3(.7,.3,.0):ampm>19?vec3(.001):vec3(.6);
vec2 uv = vec2(atan(hp.z,hp.y)/PI2,p.x);
uv.xy += T*vec2(-.02,1);
vec2 sc = vec2(14.,3), id = floor(uv*sc);
uv = fract(uv*sc)-.5;
float px = 2./R.x, rnd = hash21(id);
if(rnd>.5) uv.x*=-1.;
float chk = mod(id.y + id.x,2.) * 2. - 1.;
vec2 gx = length(uv-.5)<length(uv+.5)? vec2(uv-.5) : vec2(uv+.5);
float tr = length(gx)-.5;
if(ampm>19){
tr= smoothstep(-px,px,abs(abs(tr)-.1)-.05);
}else{
tr= (chk>.5 ^^ rnd<.5) ? smoothstep(-px,px,tr) : smoothstep(px,-px,tr);
}
h = mix(clr, cld,tr);
}
C = h * diff;
}
vec3 clr = ampm<12?vec3(0.306,0.118,0.016):ampm>19?vec3(0.039,0.020,0.078):vec3(.25);
C = mix(C,clr, 1.-exp(-.00325*d*d*d));
if((int(F.x)%6 == int(F.y)%6) && R.x>800. ) C = clr;
C = mix(C,clamp(C*.8,vec3(0),vec3(1)),hash21(floor(uv.xy*30.)+uv));
C = pow(C, vec3(.4545));
fragColor = vec4(C,1);
}
</script>
<script src="https://fecoder-pic-1302080640.cos.ap-nanjing.myqcloud.com/twgl.min.js"></script>
<script>
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true,
});
} else {
obj[key] = value;
}
return obj;
}
class Mouse {
constructor(element) {
_defineProperty(
this,
"reset",
() => {
this.x =
~~(document.documentElement.clientWidth,
window.innerWidth || 0) / 2;
this.y =
~~(document.documentElement.clientHeight,
window.innerHeight || 0) / 2;
}
);
this.element = element || window;
this.drag = false;
this.x =
~~(document.documentElement.clientWidth, window.innerWidth || 0) /
2;
this.y =
~~(document.documentElement.clientHeight, window.innerHeight || 0) /
2;
this.getCoordinates = this.getCoordinates.bind(this);
this.events = ["mouseenter", "mousemove"];
this.events.forEach((eventName) => {
this.element.addEventListener(eventName, this.getCoordinates);
});
this.element.addEventListener("mousedown", () => {
this.drag = true;
});
this.element.addEventListener("mouseup", () => {
this.drag = false;
});
window.addEventListener("resize", this.reset);
}
getCoordinates(event) {
event.preventDefault();
if (this.drag) {
this.x = event.pageX;
this.y = event.pageY;
}
}
}
const glcanvas = document.getElementById("canvas");
const gl = glcanvas.getContext("webgl2");
const programInfo = twgl.createProgramInfo(gl, [
"vertexShader",
"fragmentShader",
]);
const arrays = {
position: [-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0],
};
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
const mouse = new Mouse(glcanvas);
let umouse = [gl.canvas.width / 2, gl.canvas.height / 2, 0, 0];
let tmouse = umouse;
let uniforms;
const ct = new Date();
const timenow =
ct.getHours() * 60.0 * 60 +
ct.getMinutes() * 60 +
ct.getSeconds() +
ct.getMilliseconds() / 1000.0;
const render = (time) => {
twgl.resizeCanvasToDisplaySize(gl.canvas, 1.0);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
const factor = 0.15;
umouse = [mouse.x, mouse.y, 0];
tmouse[0] = tmouse[0] - (tmouse[0] - umouse[0]) * factor;
tmouse[1] = tmouse[1] - (tmouse[1] - umouse[1]) * factor;
tmouse[2] = mouse.drag ? 1 : -1;
const mytime = time / 1000;
uniforms = {
u_time: mytime,
u_mouse: tmouse,
u_date: mytime + timenow,
u_resolution: [gl.canvas.width, gl.canvas.height],
};
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, bufferInfo);
requestAnimationFrame(render);
};
window.addEventListener("DOMContentLoaded", (event) => {
requestAnimationFrame(render);
});
</script>
</body>
</html>