WebGL Motion Blur Compositing

by balefrost

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <canvas id="canvas" width="1920" height="1080" style="width: 100%; height: auto"></canvas>
    <br />
    <button id="renderBtn">Render</button>
</body>
</html>

JavaScript

const SAMPLES = 32;
const OPTION = 'webgl';

let pos = 130;

//Setup testing page
window.onload = ()=>{
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d', {willReadFrequently: true});

    draw(ctx);

    document.getElementById('renderBtn').addEventListener('click', ()=>{
        pos = 130;
        draw(ctx);
        webGLComposite(canvas, ctx);
    });
}

//Generates a simple image purely for testing. Treat this function as a black box
function draw(ctx){
    ctx.fillStyle = '#22ffff';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    ctx.fillStyle = '#FFFF66';
    ctx.beginPath();
    ctx.arc(pos, 1080/4, 100, 0, Math.PI * 2);
    ctx.fill();

    ctx.fillStyle = '#000';
    ctx.beginPath();
    ctx.arc(pos, 1080/4*3, 100, 0, Math.PI * 2);
    ctx.fill();
}

function webGLComposite(canvas, ctx){
    const sampleCanvas = new OffscreenCanvas(canvas.width, canvas.height);
    const sampleCtx = sampleCanvas.getContext('2d');

    //Setup webgl shit
    const createShader = (gl, type, source)=>{
        const shader = gl.createShader(type);

        gl.shaderSource(shader, source);
        gl.compileShader(shader);

        if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            return shader;
        }

        console.log(gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return undefined;
    };
    const createProgram = (gl, vs, fs)=>{
        const prg = gl.createProgram();
        gl.attachShader(prg, vs);
        gl.attachShader(prg, fs);
        gl.linkProgram(prg);

        if (gl.getProgramParameter(prg, gl.LINK_STATUS)){
            return prg;
        }

        console.log(gl.getProgramInfoLog(prg));
        return undefined;
    };
    const blurCanvas = new OffscreenCanvas(canvas.width, canvas.height);
    const gl = blurCanvas.getContext('webgl2', {preserveDrawingBuffer: true});
    const vs = createShader(gl, gl.VERTEX_SHADER, `#version 300 es
        in vec4...