Created
May 2, 2022 17:44
-
-
Save greggman/398b5b5505ad1938a8cae6201f9e5d28 to your computer and use it in GitHub Desktop.
WebGL - 2 Textures - with a video (transparency) with gui
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
@import url("https://webglfundamentals.org/webgl/resources/webgl-tutorials.css"); | |
body { | |
margin: 0; | |
} | |
canvas { | |
width: 100vw; | |
height: 100vh; | |
display: block; | |
background-image: url(https://mdn.github.io/dom-examples/canvas/chroma-keying/media/foo.png); | |
} |
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
<button type="button">click to play</button> | |
<canvas id="canvas"></canvas> | |
<!-- vertex shader --> | |
<script id="vertex-shader-2d" type="x-shader/x-vertex"> | |
attribute vec2 a_position; | |
attribute vec2 a_texCoord; | |
uniform vec2 u_resolution; | |
varying vec2 v_texCoord; | |
void main() { | |
// convert the rectangle from pixels to 0.0 to 1.0 | |
vec2 zeroToOne = a_position / u_resolution; | |
// convert from 0->1 to 0->2 | |
vec2 zeroToTwo = zeroToOne * 2.0; | |
// convert from 0->2 to -1->+1 (clipspace) | |
vec2 clipSpace = zeroToTwo - 1.0; | |
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); | |
// pass the texCoord to the fragment shader | |
// The GPU will interpolate this value between points. | |
v_texCoord = a_texCoord; | |
} | |
</script> | |
<!-- fragment shader --> | |
<script id="fragment-shader-2d" type="x-shader/x-fragment"> | |
precision mediump float; | |
// our textures | |
uniform sampler2D u_image; | |
uniform vec3 minColor; | |
// the texCoords passed in from the vertex shader. | |
varying vec2 v_texCoord; | |
void main() { | |
vec4 color = texture2D(u_image, v_texCoord); | |
if (all(lessThan(color.rgb, minColor))) { | |
color = vec4(0); | |
} | |
gl_FragColor = color; | |
// premultiply alpha | |
gl_FragColor.rgb *= gl_FragColor.a; | |
} | |
</script><!-- | |
for most samples webgl-utils only provides shader compiling/linking and | |
canvas resizing because why clutter the examples with code that's the same in every sample. | |
See https://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html | |
and https://webglfundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html | |
for webgl-utils, m3, m4, and webgl-lessons-ui. | |
--> | |
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script> |
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
// WebGL - 2 Textures | |
// from https://webglfundamentals.org/webgl/webgl-2-textures.html | |
import GUI from 'https://cdn.jsdelivr.net/npm/lil-gui@0.16/+esm'; | |
function loadVideo(url) { | |
return new Promise((resolve, reject) => { | |
var video = document.createElement('video'); | |
video.autoplay = true; | |
requestCORSIfNotSameOrigin(video, url) | |
video.src = url; | |
video.volume = 0; | |
video.loop = true; | |
video.addEventListener('playing', waitForFrame(video, resolve)); | |
video.onerror = reject; | |
video.play(); | |
return video; | |
}); | |
} | |
function waitForFrame(video, resolve) { | |
const id = setInterval(() => { | |
if (video.currentTime > 0.1 && video.videoWidth > 0) { | |
clearInterval(id); | |
resolve(video); | |
} | |
}); | |
} | |
async function loadImage(url) { | |
const img = new Image(); | |
requestCORSIfNotSameOrigin(img, url) | |
img.src = url; | |
await img.decode(); | |
return img; | |
} | |
async function main() { | |
const images = await Promise.all([ | |
loadVideo("https://mdn.github.io/dom-examples/canvas/chroma-keying/media/video.mp4"), | |
]); | |
render(images); | |
} | |
const b = document.querySelector('button'); | |
b.addEventListener('click', () => { | |
b.style.display = 'none'; | |
main(); | |
}); | |
function render(images) { | |
// Get A WebGL context | |
/** @type {HTMLCanvasElement} */ | |
var canvas = document.querySelector("#canvas"); | |
var gl = canvas.getContext("webgl"); | |
if (!gl) { | |
return; | |
} | |
// setup GLSL program | |
var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-2d", "fragment-shader-2d"]); | |
gl.useProgram(program); | |
// look up where the vertex data needs to go. | |
var positionLocation = gl.getAttribLocation(program, "a_position"); | |
var texcoordLocation = gl.getAttribLocation(program, "a_texCoord"); | |
// Create a buffer to put three 2d clip space points in | |
var positionBuffer = gl.createBuffer(); | |
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer) | |
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); | |
// Set a rectangle the same size as the image. | |
setRectangle(gl, 0, 0, images[0].videoWidth, images[0].videoHeight); | |
// provide texture coordinates for the rectangle. | |
var texcoordBuffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer); | |
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ | |
0.0, 0.0, | |
1.0, 0.0, | |
0.0, 1.0, | |
0.0, 1.0, | |
1.0, 0.0, | |
1.0, 1.0, | |
]), gl.STATIC_DRAW); | |
const minColor = [10, 10, 10]; | |
const gui = new GUI(); | |
gui.add(minColor, '0', 0, 255, 1).name('red'); | |
gui.add(minColor, '1', 0, 255, 1).name('green'); | |
gui.add(minColor, '2', 0, 255, 1).name('blue'); | |
// create 2 textures | |
var textures = []; | |
for (var ii = 0; ii < 1; ++ii) { | |
var texture = gl.createTexture(); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
// Set the parameters so we can render any size image. | |
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); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); | |
// Upload the image into the texture. | |
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, images[ii]); | |
// add the texture to the array of textures. | |
textures.push(texture); | |
} | |
// lookup uniforms | |
var resolutionLocation = gl.getUniformLocation(program, "u_resolution"); | |
var minColorLocation = gl.getUniformLocation(program, "minColor"); | |
// lookup the sampler locations. | |
var u_imageLocation = gl.getUniformLocation(program, "u_image"); | |
function renderLoop() { | |
webglUtils.resizeCanvasToDisplaySize(gl.canvas); | |
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); | |
// Clear the canvas | |
gl.clearColor(0, 0, 0, 1); | |
gl.clear(gl.COLOR_BUFFER_BIT); | |
// Tell it to use our program (pair of shaders) | |
gl.useProgram(program); | |
// Turn on the position attribute | |
gl.enableVertexAttribArray(positionLocation); | |
// Bind the position buffer. | |
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); | |
// Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER) | |
var size = 2; // 2 components per iteration | |
var type = gl.FLOAT; // the data is 32bit floats | |
var normalize = false; // don't normalize the data | |
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position | |
var offset = 0; // start at the beginning of the buffer | |
gl.vertexAttribPointer( | |
positionLocation, size, type, normalize, stride, offset); | |
// Turn on the texcoord attribute | |
gl.enableVertexAttribArray(texcoordLocation); | |
// bind the texcoord buffer. | |
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer); | |
// Tell the texcoord attribute how to get data out of texcoordBuffer (ARRAY_BUFFER) | |
var size = 2; // 2 components per iteration | |
var type = gl.FLOAT; // the data is 32bit floats | |
var normalize = false; // don't normalize the data | |
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position | |
var offset = 0; // start at the beginning of the buffer | |
gl.vertexAttribPointer( | |
texcoordLocation, size, type, normalize, stride, offset); | |
// set the resolution | |
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height); | |
gl.uniform3fv(minColorLocation, minColor.map(v => v / 255)); | |
// set which texture units to render with. | |
gl.uniform1i(u_imageLocation, 0); // texture unit 0 | |
// upload the latest video frame | |
gl.bindTexture(gl.TEXTURE_2D, textures[0]); | |
// Upload the image into the texture. | |
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, images[0]); | |
// Set each texture unit to use a particular texture. | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, textures[0]); | |
// Draw the rectangle. | |
gl.drawArrays(gl.TRIANGLES, 0, 6); | |
requestAnimationFrame(renderLoop); | |
} | |
requestAnimationFrame(renderLoop); | |
} | |
function randomInt(range) { | |
return Math.floor(Math.random() * range); | |
} | |
function setRectangle(gl, x, y, width, height) { | |
var x1 = x; | |
var x2 = x + width; | |
var y1 = y; | |
var y2 = y + height; | |
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ | |
x1, y1, | |
x2, y1, | |
x1, y2, | |
x1, y2, | |
x2, y1, | |
x2, y2, | |
]), gl.STATIC_DRAW); | |
} | |
// This is needed if the images are not on the same domain | |
// NOTE: The server providing the images must give CORS permissions | |
// in order to be able to use the image with WebGL. Most sites | |
// do NOT give permission. | |
// See: https://webglfundamentals.org/webgl/lessons/webgl-cors-permission.html | |
function requestCORSIfNotSameOrigin(img, url) { | |
if ((new URL(url, window.location.href)).origin !== window.location.origin) { | |
img.crossOrigin = ""; | |
} | |
} |
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
{"name":"WebGL - 2 Textures - with a video (transparency) with gui","settings":{},"filenames":["index.html","index.css","index.js"]} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment