Pass 1 renders eight plasma sheets into an atlas with linear depth in alpha; pass 2 marches the refracted ray to each sheet and absorbs it by real path length, so near and far layers separate under pointer parallax. Plasma cycle repeats exactly every 30 s; - / = change speed.
50 lines
2.7 KiB
Python
50 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Inline every *.frag next to this file into playground.html (keys 1..N switch, mouse = u_mouse)."""
|
|
import glob, json, os
|
|
|
|
here = os.path.dirname(os.path.abspath(__file__))
|
|
frags = sorted(glob.glob(os.path.join(here, "*.frag")))
|
|
names = [os.path.basename(f)[:-5] for f in frags]
|
|
srcs = [open(f).read() for f in frags]
|
|
|
|
html = """<!doctype html><html><head><meta charset=utf-8><title>shaders</title>
|
|
<style>html,body{margin:0;height:100%;background:#000;overflow:hidden}
|
|
canvas{display:block;width:100vw;height:100vh}
|
|
#osd{position:fixed;left:14px;bottom:12px;font:13px ui-monospace,monospace;color:#9bf;opacity:.75;pointer-events:none}</style>
|
|
</head><body><canvas id=c></canvas><div id=osd></div><script>
|
|
const SHADERS = __SHADERS__;
|
|
const NAMES = __NAMES__;
|
|
const VS='attribute vec2 p;void main(){gl_Position=vec4(p,0,1);}';
|
|
const c=document.getElementById('c'),osd=document.getElementById('osd');
|
|
const gl=c.getContext('webgl',{antialias:false,preserveDrawingBuffer:false});
|
|
let progs=[],cur=0,mx=0.5,my=0.5,t0=performance.now();
|
|
function sh(t,s){const o=gl.createShader(t);gl.shaderSource(o,s);gl.compileShader(o);
|
|
if(!gl.getShaderParameter(o,gl.COMPILE_STATUS)){osd.textContent='compile error: '+gl.getShaderInfoLog(o);console.error(gl.getShaderInfoLog(o));}
|
|
return o;}
|
|
for(const s of SHADERS){const pr=gl.createProgram();
|
|
gl.attachShader(pr,sh(gl.VERTEX_SHADER,VS));gl.attachShader(pr,sh(gl.FRAGMENT_SHADER,s));
|
|
gl.linkProgram(pr);progs.push(pr);}
|
|
const b=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,b);
|
|
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),gl.STATIC_DRAW);
|
|
function resize(){const d=devicePixelRatio||1;c.width=innerWidth*d;c.height=innerHeight*d;}
|
|
addEventListener('resize',resize);resize();
|
|
addEventListener('mousemove',e=>{mx=e.clientX/innerWidth;my=1-e.clientY/innerHeight;});
|
|
addEventListener('keydown',e=>{if(e.key>='1'&&e.key<=String(progs.length)){cur=+e.key-1;}});
|
|
function frame(){
|
|
const pr=progs[cur];gl.useProgram(pr);
|
|
const l=gl.getAttribLocation(pr,'p');gl.enableVertexAttribArray(l);
|
|
gl.vertexAttribPointer(l,2,gl.FLOAT,false,0,0);
|
|
gl.uniform2f(gl.getUniformLocation(pr,'u_res'),c.width,c.height);
|
|
gl.uniform1f(gl.getUniformLocation(pr,'u_time'),(performance.now()-t0)/1000);
|
|
gl.uniform2f(gl.getUniformLocation(pr,'u_mouse'),mx,my);
|
|
gl.viewport(0,0,c.width,c.height);gl.drawArrays(gl.TRIANGLE_STRIP,0,4);
|
|
osd.textContent=NAMES[cur]+' ['+(cur+1)+'/'+progs.length+'] · keys 1-'+progs.length+' switch · move mouse';
|
|
requestAnimationFrame(frame);
|
|
}
|
|
frame();
|
|
</script></body></html>
|
|
"""
|
|
out = html.replace("__SHADERS__", json.dumps(srcs)).replace("__NAMES__", json.dumps(names))
|
|
open(os.path.join(here, "playground.html"), "w").write(out)
|
|
print("playground.html:", ", ".join(names))
|