Last active
March 30, 2025 22:45
-
-
Save Osmiogrzesznik/2f814920d4e2007fd4b97d14adad5a2c to your computer and use it in GitHub Desktop.
WebGl Boiler Plate for exploring video filters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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