UNPKG

fisheyegl-video

Version:

A JavaScript library for correcting fisheye, or barrel distortion, in videos in the browser with WebGL

274 lines (229 loc) 9.43 kB
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ const shaders = require('./shaders'); function compileShader(gl, vertexSrc, fragmentSrc) { function checkCompile(shader) { if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { throw new Error(gl.getShaderInfoLog(shader)); } } const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexSrc); gl.compileShader(vertexShader); checkCompile(vertexShader); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentSrc); gl.compileShader(fragmentShader); checkCompile(fragmentShader); const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); return program; } function createTexture(glContext) { const texture = glContext.createTexture(); glContext.bindTexture(glContext.TEXTURE_2D, texture); // Because video has to be download over the internet // they might take a moment until it's ready so // put a single pixel in the texture so we can // use it immediately. const level = 0; const internalFormat = glContext.RGBA; const width = 1; const height = 1; const border = 0; const srcFormat = glContext.RGBA; const srcType = glContext.UNSIGNED_BYTE; const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue glContext.texImage2D(glContext.TEXTURE_2D, level, internalFormat, width, height, border, srcFormat, srcType, pixel); // Turn off mips and set wrapping to clamp to edge so it // will work regardless of the dimensions of the video. glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_WRAP_S, glContext.CLAMP_TO_EDGE); glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_WRAP_T, glContext.CLAMP_TO_EDGE); glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_MIN_FILTER, glContext.LINEAR); glContext.bindTexture(glContext.TEXTURE_2D, texture); return texture; } const FisheyeGl = function FisheyeGl(opts) { const options = opts || {}; const model = { vertex: [ -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, ], indices: [ 0, 1, 2, 0, 2, 3, 2, 1, 0, 3, 2, 0, ], textureCoords: [ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, ], }; const dist = options.dist || { scale: 0, zoom: 1, zoomAnchor: { x: 0.5, y: 0.5 }, shift: { x: 0.0, y: 0.0 }, k3: 0, k5: 0, k7: 0, }; const { canvasBuffer } = options; const { canvasDestination } = options; const destinationContext = canvasDestination.getContext('2d'); const glContext = canvasBuffer.getContext('webgl'); glContext.viewport(0, 0, glContext.canvas.width, glContext.canvas.height); const program = compileShader(glContext, shaders.vertex, shaders.fragment4); glContext.useProgram(program); const aVertexPosition = glContext.getAttribLocation(program, 'aVertexPosition'); const aTextureCoord = glContext.getAttribLocation(program, 'aTextureCoord'); const uSampler = glContext.getUniformLocation(program, 'uSampler'); const uScale = glContext.getUniformLocation(program, 'uScale'); const uZoom = glContext.getUniformLocation(program, 'uZoom'); const uZoomAnchor = glContext.getUniformLocation(program, 'uZoomAnchor'); const uShift = glContext.getUniformLocation(program, 'uShift'); const uSize = glContext.getUniformLocation(program, 'uSize'); const uDistortion = glContext.getUniformLocation(program, 'uDistortion'); let vertexBuffer; let indexBuffer; let textureBuffer; function createBuffers() { vertexBuffer = glContext.createBuffer(); glContext.bindBuffer(glContext.ARRAY_BUFFER, vertexBuffer); glContext.bufferData(glContext.ARRAY_BUFFER, new Float32Array(model.vertex), glContext.STATIC_DRAW); glContext.bindBuffer(glContext.ARRAY_BUFFER, null); indexBuffer = glContext.createBuffer(); glContext.bindBuffer(glContext.ELEMENT_ARRAY_BUFFER, indexBuffer); glContext.bufferData(glContext.ELEMENT_ARRAY_BUFFER, new Uint16Array(model.indices), glContext.STATIC_DRAW); glContext.bindBuffer(glContext.ELEMENT_ARRAY_BUFFER, null); textureBuffer = glContext.createBuffer(); glContext.bindBuffer(glContext.ARRAY_BUFFER, textureBuffer); glContext.bufferData(glContext.ARRAY_BUFFER, new Float32Array(model.textureCoords), glContext.STATIC_DRAW); glContext.bindBuffer(glContext.ARRAY_BUFFER, null); } createBuffers(); const texture = createTexture(glContext); function applyDistortion() { glContext.clearColor(0.0, 0.0, 0.0, 1.0); glContext.enable(glContext.DEPTH_TEST); glContext.clear(glContext.COLOR_BUFFER_BIT | glContext.DEPTH_BUFFER_BIT); glContext.enableVertexAttribArray(aVertexPosition); glContext.bindBuffer(glContext.ARRAY_BUFFER, vertexBuffer); glContext.vertexAttribPointer(aVertexPosition, 3, glContext.FLOAT, false, 0, 0); glContext.enableVertexAttribArray(aTextureCoord); glContext.bindBuffer(glContext.ARRAY_BUFFER, textureBuffer); glContext.vertexAttribPointer(aTextureCoord, 2, glContext.FLOAT, false, 0, 0); glContext.activeTexture(glContext.TEXTURE0); glContext.bindTexture(glContext.TEXTURE_2D, texture); glContext.uniform1i(uSampler, 0); glContext.uniform1f(uScale, dist.scale); glContext.uniform1f(uZoom, dist.zoom); glContext.uniform2fv(uZoomAnchor, [dist.zoomAnchor.x, dist.zoomAnchor.y]); glContext.uniform2fv(uShift, [dist.shift.x, dist.shift.y]); glContext.uniform2fv(uSize, [glContext.drawingBufferWidth, glContext.drawingBufferHeight]); glContext.uniform3fv(uDistortion, [dist.k3, dist.k5, dist.k7]); glContext.bindBuffer(glContext.ELEMENT_ARRAY_BUFFER, indexBuffer); glContext.drawElements(glContext.TRIANGLES, model.indices.length, glContext.UNSIGNED_SHORT, 0); destinationContext.drawImage(canvasBuffer, 0, 0); } function updateVideoFrame(video) { const level = 0; const internalFormat = glContext.RGBA; const srcFormat = glContext.RGBA; const srcType = glContext.UNSIGNED_BYTE; glContext.texImage2D(glContext.TEXTURE_2D, level, internalFormat, srcFormat, srcType, video); applyDistortion(); } const distorter = { options, dist, applyDistortion, updateVideoFrame, }; return distorter; }; if (typeof (document) !== 'undefined') { window.FisheyeGl = FisheyeGl; } module.exports = FisheyeGl; },{"./shaders":2}],2:[function(require,module,exports){ module.exports = { fragment4: require('./shaders/fragment4.glfs'), vertex: require('./shaders/vertex.glvs') }; },{"./shaders/fragment4.glfs":3,"./shaders/vertex.glvs":4}],3:[function(require,module,exports){ module.exports = "\ #ifdef GL_ES\n\ precision highp float;\n\ #endif\n\ uniform vec2 uSize;\n\ uniform vec3 uDistortion;\n\ uniform float uScale;\n\ uniform float uZoom;\n\ uniform vec2 uZoomAnchor;\n\ uniform vec2 uShift;\n\ uniform sampler2D uSampler;\n\ varying vec3 vPosition;\n\ varying vec2 vTextureCoord;\n\ vec2 GLCoord2TextureCoord(vec2 glCoord) {\n\ return glCoord * vec2(1.0, -1.0) + vec2(0.5, 0.5);\n\ }\n\ void main(void){\n\ float scale = uScale;\n\ float zoom = uZoom;\n\ vec2 zoomAnchor = uZoomAnchor;\n\ vec2 shift = uShift;\n\ vec3 vPos;\n\ vPos[0] = vPosition[0] / zoom + 2.0 * (zoomAnchor[0] - 0.5);\n\ vPos[1] = vPosition[1] / zoom - 2.0 * (zoomAnchor[1] - 0.5);\n\ float k3 = uDistortion[0] / 200.0;\n\ float k5 = uDistortion[1] / 200.0;\n\ float k7 = uDistortion[2] / 200.0;\n\ vec2 vMapping = vPos.xy;\n\ vMapping.x *= uSize[0] / 2.0; \n\ vMapping.y *= uSize[1] / 2.0; \n\ float centerX = uSize[0] * (shift.x / 200.0);\n\ float centerY = - uSize[1] * (shift.y / 200.0);\n\ float off_x = vMapping.x - centerX;\n\ float off_y = vMapping.y - centerY;\n\ float r2 = (off_x * off_x) + (off_y * off_y);\n\ r2 *= 4.0 / (uSize[0] * uSize[0] + uSize[1] * uSize[1]);\n\ float r4 = r2 * r2;\n\ float r6 = r2 * r2 * r2;\n\ float rescale = pow(2.0, - scale / 100.0);\n\ float radius_mult = rescale * (k3 * r2 + k5 * r4 + k7 * r6 + 1.0);\n\ vMapping.x = (centerX + radius_mult * off_x) / uSize[0];\n\ vMapping.y = (centerY + radius_mult * off_y) / uSize[1];\n\ vMapping = GLCoord2TextureCoord(vMapping);\n\ vec4 texture = texture2D(uSampler, vMapping);\n\ if(vMapping.x > 0.9999 || vMapping.x < 0.0001 || vMapping.y > 0.9999 || vMapping.y < 0.0001){\n\ texture = vec4(0.0, 0.0, 0.0, 1.0);\n\ }\n\ gl_FragColor = texture;\n\ }\n\ "; },{}],4:[function(require,module,exports){ module.exports = "\ #ifdef GL_ES\n\ precision highp float;\n\ #endif\n\ attribute vec3 aVertexPosition;\n\ attribute vec2 aTextureCoord;\n\ varying vec3 vPosition;\n\ varying vec2 vTextureCoord;\n\ void main(void){\n\ vPosition = aVertexPosition;\n\ vTextureCoord = aTextureCoord;\n\ gl_Position = vec4(vPosition,1.0);\n\ }\n\ "; },{}]},{},[1]);