Skip to content

Instantly share code, notes, and snippets.

@Osmiogrzesznik
Last active March 30, 2025 22:45
Show Gist options
  • Save Osmiogrzesznik/2f814920d4e2007fd4b97d14adad5a2c to your computer and use it in GitHub Desktop.
Save Osmiogrzesznik/2f814920d4e2007fd4b97d14adad5a2c to your computer and use it in GitHub Desktop.
WebGl Boiler Plate for exploring video filters
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>
this_is_at =
`
https://gist.github.com/Osmiogrzesznik/2f814920d4e2007fd4b97d14adad5a2c
`
window.onerror = function(...x) {
x[2] = x[2] - 8;
console.error(x);
}
say = console.log
</script>
<title>WebGL Video Shader</title>
<style>
body { text-align: center; background: black; color: white; margin:0; }
canvas { display: block; width:100vw; margin-top:10; }
.controls { display: flex; justify-content: center; flex-wrap: wrap; gap: 10px; }
#frag,#fragDefs{
display: none;
}
btn{
border:1px solid cyan;
padding:10px;
background:#ddaa55;
}
</style>
</head>
<body>
<pre id="fragDefs">
#define PI 3.141592653589793
precision mediump float;
uniform sampler2D uvid;
uniform vec2 resolution;
uniform vec3 keyColor;
varying vec2 vTexCoord;
// https://www.shadertoy.com/view/Xt23Ry
float rand(float co) { return fract(sin(co*(91.3458)) * 47453.5453); }
float rand(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); }
float rand(vec3 co){ return rand(co.xy+rand(co.z)); }
</pre>
<script type="shader" id="frag">
// expects values in the range of [0,1]x[0,1], returns values in the [0,1] range.
// do not collapse into a single function per: http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/
highp float rand3js( const in vec2 uvin ) {
const lowp float a = 12.9891118, b = 78.21133, c = 43758.5453111;
highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );
return fract( sin( sn ) * c+ub);
}
void main() {
vec2 uv = vTexCoord;
vec4 uv3 = gl_FragCoord;
uv3.xy /= resolution;
float x = uv3.x;
float y = uv3.y;
float z = rand(uv3.xy);
uv3.x = x;
uv3.y = y;
uv3.z = z;
uv3.w = 1.;
gl_FragColor = uv3;
}
</script>
<video id="video" src="../../../DCIM/Camera/40.mp4"
cross-origin="anonymous"
loop
muted
autoplay
style="display:none;">
</video>
<canvas id="glCanvas"></canvas>
<input type="file" id="videoInput">
<btn onclick = "play(!(pg++ % 2) )">dddd</btn>
<div class="controls" id="controls"></div>
<script>
var pg=0;
// WebGL setup
const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl2');
if (!gl) { alert('WebGL not supported'); }
// Fragment Shader with dynamic uniforms
let fragShaderSrc = fragDefs.innerText;
const controlsContainer = document.getElementById('controls');
const uniforms = {};
//=-------=-------=-------=-------=-------=-------=-------=-------=-------;;;;;;;;;=-------=-------=-------;
// Adding uniforms here
addUniformControl("ua", 0.0, 3.0, 0.01, 1.0);
addUniformControl("ub", 0.0, 3.0, 0.01, 1.0);
//posterize colors
// -=-------=-------=-------=-------=-------=-------=-------=-------=-------=-------=-------=-------=-------=-------=-------=-------=-------=-------
say(frag.innerText)
fragShaderSrc += frag.innerText;
let program, videoTexture, positionBuffer, texCoordBuffer, vao, uniformLocations = {};
// Video setup
video.crossOrigin = "anonymous";
video.loop = true;
video.muted = true;
video.style.display = 'none';
document.getElementById('videoInput').addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
const url = URL.createObjectURL(file);
video.src = url;
video.play();
}
});
video.addEventListener('loadedmetadata', () => {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
initWebGL();
});
function addUniformControl(id, min, max, step, value,func) {
const input = document.createElement('input');
input.type = 'range';
input.id = id;
input.min = min;
input.max = max;
input.step = step;
input.value = value;
const label = document.createElement('label');
label.innerText = id;
label.htmlFor = id;
controlsContainer.appendChild(label);
controlsContainer.appendChild(input);
if(!func){
uniforms[id] = parseFloat(value);
input.addEventListener('input', (e) => {
uniforms[id] = parseFloat(e.target.value);
console.log('uniform:',id,'is',uniforms[id] )
});
}else{
uniforms[id] = {func:function(){return func(input.value)}}
}
fragShaderSrc += `uniform float ${id};\n`;
}
function initWebGL() {
// Create shaders
const vsSource = `
attribute vec4 position;
attribute vec2 texCoord;
varying vec2 vTexCoord;
void main() {
gl_Position = position;
vTexCoord = texCoord;
}
`;
const vertexShader = compileShader(gl.VERTEX_SHADER, vsSource);
const fragmentShader = compileShader(gl.FRAGMENT_SHADER, fragShaderSrc);
// Create program
program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error("Shader link error: " + gl.getProgramInfoLog(program));
return;
}
gl.useProgram(program);
// Create buffers
positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, 1, -1, -1, 1,
-1, 1, 1, -1, 1, 1
]), gl.STATIC_DRAW);
texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 1, 1, 1, 0, 0,
0, 0, 1, 1, 1, 0
]), gl.STATIC_DRAW);
vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const posLoc = gl.getAttribLocation(program, "position");
gl.enableVertexAttribArray(posLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
const texLoc = gl.getAttribLocation(program, "texCoord");
gl.enableVertexAttribArray(texLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.vertexAttribPointer(texLoc, 2, gl.FLOAT, false, 0, 0);
// Texture setup
videoTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, videoTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
// Uniform locations
uniformLocations.resolution = gl.getUniformLocation(program, "resolution");
uniformLocations.keyColor = gl.getUniformLocation(program, "keyColor");
gl.uniform3f(uniformLocations.keyColor, 0.0, 0.0, 0.0); // Default color
for (let key in uniforms) {
uniformLocations[key] = gl.getUniformLocation(program, key);
}
render();
}
function compileShader(type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error("Shader error: " + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
function play(tt){
// video.currentTime = 0;
if(tt){
video.play()
}else{
video.pause()
}
}
function render() {
requestAnimationFrame(render);
gl.useProgram(program);
gl.bindVertexArray(vao);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, videoTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, video);
gl.uniform2f(uniformLocations.resolution, canvas.width, canvas.height);
for (let key in uniforms) {
if(!uniforms[key].func){
gl.uniform1f(uniformLocations[key], uniforms[key]);
}else{
let funcout = uniforms[key].func();
gl.uniform1f(uniformLocations[key], funcout);
}
}
gl.drawArrays(gl.TRIANGLES, 0, 6);
if(flagreadpixel){
const pixel = new Uint8Array(4); // [R, G, B, A]
gl.readPixels(glX, glY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
pickedcolor = `rgb(${pixel[0]}, ${pixel[1]}, ${pixel[2]})`;
const r = pixel[0] / 255;
const g = pixel[1] / 255;
const b = pixel[2] / 255;
gl.uniform3f(uniformLocations.keyColor, r,g,b); // Default color
document.body.style.background = pickedcolor;
console.log("Picked color:", pickedcolor);
flagreadpixel = 0;
}
}
var pickedcolor;
var flagreadpixel = 0;
var glX,glY;
function pickColor(event) {
const rect = canvas.getBoundingClientRect()
let ch = rect.bottom - rect.top;
let cw = rect.right - rect.left;
let ratY = canvas.height/ch;
let ratX = canvas.width/cw;
let x = event.offsetX;
let y = event.offsetY;
console.log("x: " + x + " y: " + y)
let coords = getCanvasCoordinates(event)
ppx = x*ratX// (event.clientX/wiw)*canvas.height/2;
ppy =canvas.height - y*ratY;//(canvas.height - event.offsetY*2.7) ;
//ppy = event.offsetY/canvas.height
ppx = ppx | 0
ppy = ppy| 0
glX = ppx;
glY = ppy;
// Convert to WebGL coordinates
//glX = Math.round(x * gl.canvas.width / rect.width);
// glY = Math.round((rect.height - y) * gl.canvas.height / rect.height);
flagreadpixel = 1;
}
function getCanvasCoordinates(clickEvent) {
const canvasX = clickEvent.pageX - canvas.offsetLeft;
const canvasY = clickEvent.pageY - canvas.offsetTop;
return { x: canvasX, y: canvasY };
}
// Attach event listener to the canvas
canvas.addEventListener("click", (event) => {
const pickedColor = pickColor(event);
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment