var glRef = {}; function loadImage(src) { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => { resolve(img); }; img.src = src; }); } // The required resolution is passed as parameters const initWebGL = async ({ width, height }) => { const backgroundImage = await loadImage("./seaside.png"); const canvas = document.querySelector("#canvas") || document.createElement("canvas"); canvas.width = width; canvas.height = height; const gl = canvas.getContext("webgl2"); if (!gl) { return; } glRef.gl = gl; // setup GLSL program, for simplicity they are stored in HTML script tags // use webglUtils from https://webgl2fundamentals.org/ const program = webglUtils.createProgramFromScripts(gl, [ "vertex-shader", "fragment-shader", ]); gl.useProgram(program); const positionLocation = gl.getAttribLocation(program, "a_position"); const texcoordLocation = gl.getAttribLocation(program, "a_texCoord"); const positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // Set a rectangle the same size as the image. const x1 = 0; const x2 = 0 + width; const y1 = 0; const y2 = 0 + height; gl.bufferData( gl.ARRAY_BUFFER, new Float32Array([x1, y1, x2, y1, x1, y2, x1, y2, x2, y1, x2, y2]), gl.STATIC_DRAW ); // provide texture coordinates for the rectangle. const 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 textures = []; // Create a texture for the background image { const 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, backgroundImage ); textures[0] = texture; } // create texture for the video frame { const 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); const pixelData = new Uint8Array(1.5 * width * height); // Upload the image into the texture. gl.texImage2D( gl.TEXTURE_2D, 0, gl.LUMINANCE, // <-- monochrome texture, not RGBA!!! width, height * 1.5, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, pixelData, 0 ); textures[1] = texture; } const u_image = gl.getUniformLocation(program, "u_image"); const u_image_frame = gl.getUniformLocation(program, "u_image_frame"); // set which texture units to render with. gl.uniform1i(u_image, 0); // texture unit 0 gl.uniform1i(u_image_frame, 1); // texture unit 1 gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, textures[0]); gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, textures[1]); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); // 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) { const size = 2; const type = gl.FLOAT; const normalize = false; const stride = 0; const offset = 0; 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) { const size = 2; const type = gl.FLOAT; const normalize = false; const stride = 0; const offset = 0; gl.vertexAttribPointer( texcoordLocation, size, type, normalize, stride, offset ); } // lookup uniforms const resolutionLocation = gl.getUniformLocation(program, "u_resolution"); // set the resolution gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height); // Draw the rectangle. gl.drawArrays(gl.TRIANGLES, 0, 6); };