From 51e6c70fc8f47bd07501529dbb65f9f90c5d5dab Mon Sep 17 00:00:00 2001 From: Chinmay Pandhare Date: Sun, 16 Jul 2017 15:39:38 +0530 Subject: [PATCH] Added FisheyeGl Module --- dist/image-sequencer.js | 419 +++++++++++++++++- src/AddStep.js | 1 + src/Modules.js | 3 +- src/modules/FisheyeGl/Module.js | 49 ++ src/modules/FisheyeGl/fisheyegl.js | 295 ++++++++++++ .../FisheyeGl/shaders/fragment3.glfs.js | 37 ++ src/modules/FisheyeGl/shaders/vertex.glvs.js | 12 + 7 files changed, 807 insertions(+), 9 deletions(-) create mode 100644 src/modules/FisheyeGl/Module.js create mode 100644 src/modules/FisheyeGl/fisheyegl.js create mode 100644 src/modules/FisheyeGl/shaders/fragment3.glfs.js create mode 100644 src/modules/FisheyeGl/shaders/vertex.glvs.js diff --git a/dist/image-sequencer.js b/dist/image-sequencer.js index d719f912..f2a07158 100644 --- a/dist/image-sequencer.js +++ b/dist/image-sequencer.js @@ -34570,6 +34570,7 @@ function AddStep(ref, image, name, o) { o.selector = o_.selector || 'ismod-' + name; o.container = o_.container || ref.options.selector; o.image = image; + o.inBrowser = ref.options.inBrowser; var module = ref.modules[name].call(ref.images,o); ref.images[image].steps.push(module); @@ -35033,10 +35034,11 @@ module.exports = { 'do-nothing-pix': require('./modules/DoNothingPix/Module.js'), 'invert': require('./modules/Invert/Module'), 'crop': require('./modules/Crop/Module'), - 'segmented-colormap': require('./modules/SegmentedColormap/Module') + 'segmented-colormap': require('./modules/SegmentedColormap/Module'), + 'fisheye-gl': require('./modules/FisheyeGl/Module') } -},{"./modules/Crop/Module":123,"./modules/DoNothing/Module":124,"./modules/DoNothingPix/Module.js":125,"./modules/GreenChannel/Module":126,"./modules/Invert/Module":127,"./modules/NdviRed/Module":128,"./modules/SegmentedColormap/Module":129}],120:[function(require,module,exports){ +},{"./modules/Crop/Module":123,"./modules/DoNothing/Module":124,"./modules/DoNothingPix/Module.js":125,"./modules/FisheyeGl/Module":126,"./modules/GreenChannel/Module":130,"./modules/Invert/Module":131,"./modules/NdviRed/Module":132,"./modules/SegmentedColormap/Module":133}],120:[function(require,module,exports){ function ReplaceImage(ref,selector,steps,options) { if(!ref.options.inBrowser) return false; // This isn't for Node.js this_ = ref; @@ -35257,7 +35259,408 @@ module.exports = function DoNothingPix(options) { } } -},{"../_nomodule/PixelManipulation.js":131}],126:[function(require,module,exports){ +},{"../_nomodule/PixelManipulation.js":135}],126:[function(require,module,exports){ +/* + * Creates Fisheye Effect + */ +module.exports = function DoNothing(options) { + options = options || {}; + options.title = "Fisheye GL"; + var output + + function draw(input,callback) { + this_ = this; + if (!options.inBrowser) { // This module is only for browser + this.output = input; + callback(); + } + else { + + if (!document.querySelector('#image-sequencer-canvas')) { + var canvas = document.createElement('canvas'); + canvas.style.display = "none"; + canvas.setAttribute('id','image-sequencer-canvas'); + document.body.append(canvas); + } + + distorter = require('./fisheyegl.js')({ + selector: "#image-sequencer-canvas" + }); + + distorter.lens.a = options.a || distorter.lens.a; + distorter.lens.b = options.b || distorter.lens.b; + distorter.lens.Fx = options.Fx || distorter.lens.Fx; + distorter.lens.Fy = options.Fy || distorter.lens.Fy; + distorter.lens.scale = options.scale || distorter.lens.scale; + distorter.fov.x = options.x || distorter.fov.x; + distorter.fov.y = options.y || distorter.fov.y; + + distorter.setImage(input.src,function(){ + this_.output = {src: canvas.toDataURL(), format: input.format}; + callback(); + }); + + } + } + + return { + options: options, + draw: draw, + output: output + } +} + +},{"./fisheyegl.js":127}],127:[function(require,module,exports){ +module.exports = function FisheyeGl (options){ + + // Defaults: + options = options || {}; + + options.width = options.width || 800; + options.height = options.height || 600; + + var model = options.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 + ] + }; + + var lens = options.lens || { + a : 1.0, + b : 1.0, + Fx : 0.0, + Fy : 0.0, + scale : 1.5 + }; + var fov = options.fov || { + x : 1.0, + y : 1.0 + } + var image = options.image || "images/barrel-distortion.png"; + + var selector = options.selector || "#canvas"; + var gl = getGLContext(selector); + + var vertexSrc = require("./shaders/vertex.glvs"); + var fragmentSrc = require("./shaders/fragment3.glfs"); + + var program = compileShader(gl, vertexSrc, fragmentSrc) + gl.useProgram(program); + + var aVertexPosition = gl.getAttribLocation(program, "aVertexPosition"); + var aTextureCoord = gl.getAttribLocation(program, "aTextureCoord"); + var uSampler = gl.getUniformLocation(program, "uSampler"); + var uLensS = gl.getUniformLocation(program, "uLensS"); + var uLensF = gl.getUniformLocation(program, "uLensF"); + var uFov = gl.getUniformLocation(program, "uFov"); + + var vertexBuffer, + indexBuffer, + textureBuffer; + + function createBuffers() { + + vertexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(model.vertex), gl.STATIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + + indexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(model.indices), gl.STATIC_DRAW); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + + textureBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(model.textureCoords), gl.STATIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + + } + + createBuffers(); + + function getGLContext(selector){ + var canvas = document.querySelector(selector); + + if(canvas == null){ + throw new Error("there is no canvas on this page"); + } + + var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; + for (var i = 0; i < names.length; ++i) { + var gl; + try { + gl = canvas.getContext(names[i], { preserveDrawingBuffer: true }); + } catch(e) { + continue; + } + if (gl) return gl; + } + + throw new Error("WebGL is not supported!"); + } + + function compileShader(gl, vertexSrc, fragmentSrc){ + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, vertexSrc); + gl.compileShader(vertexShader); + + _checkCompile(vertexShader); + + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragmentShader, fragmentSrc); + gl.compileShader(fragmentShader); + + _checkCompile(fragmentShader); + + var program = gl.createProgram(); + + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + + gl.linkProgram(program); + + return program; + + function _checkCompile(shader){ + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + throw new Error(gl.getShaderInfoLog(shader)); + } + } + } + + function loadFile(url, callback){ + var ajax = new XMLHttpRequest(); + + if(callback) { + ajax.addEventListener("readystatechange", on) + ajax.open("GET", url, true); + ajax.send(null); + } else { + ajax.open("GET", url, false); + ajax.send(null); + + if(ajax.status == 200){ + return ajax.responseText; + } + } + + function on(){ + if(ajax.readyState === 4){ + //complete requset + if(ajax.status === 200){ + //not error + callback(null, ajax.responseText); + } else { + callback(new Error("fail to load!")); + } + } + } + } + + function loadImage(gl, img, callback, texture){ + texture = texture || gl.createTexture(); + + gl.bindTexture(gl.TEXTURE_2D, texture); + + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); //gl.NEAREST is also allowed, instead of gl.LINEAR, as neither mipmap. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); //Prevents s-coordinate wrapping (repeating). + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); //Prevents t-coordinate wrapping (repeating). + //gl.generateMipmap(gl.TEXTURE_2D); + gl.bindTexture(gl.TEXTURE_2D, null); + + if(callback) callback(null, texture); + return texture; + } + + function loadImageFromUrl(gl, url, callback){ + var texture = gl.createTexture(); + var img = new Image(); + img.addEventListener("load", function onload(){ + loadImage(gl, img, callback, texture); + options.width = img.width; + options.height = img.height; + resize( + options.width, + options.height + ) + }); + img.src = url; + return texture; + } + + function run(animate, callback){ + var f = window.requestAnimationFrame || window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; + + // ugh + if(animate === true){ + if(f){ + f(on); + } else { + throw new Error("do not support 'requestAnimationFram'"); + } + } else { + f(on); + } + + var current = null; + function on(t){ + if(!current) current = t; + var dt = t - current; + current = t; + options.runner(dt); + if (callback) callback(); + if (animate === true) f(on); + } + } + + function resize(w, h) { + gl.viewport(0, 0, w, h); + gl.canvas.width = w; + gl.canvas.height = h; + } + + options.runner = options.runner|| function runner(dt){ + + gl.clearColor(0.0, 0.0, 0.0, 1.0); + gl.enable(gl.DEPTH_TEST); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + gl.enableVertexAttribArray(aVertexPosition); + + gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); + gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0); + + gl.enableVertexAttribArray(aTextureCoord); + + gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer); + gl.vertexAttribPointer(aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.uniform1i(uSampler, 0); + + gl.uniform3fv(uLensS, [lens.a, lens.b, lens.scale]); + gl.uniform2fv(uLensF, [lens.Fx, lens.Fy]); + gl.uniform2fv(uFov, [fov.x, fov.y]); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); + gl.drawElements(gl.TRIANGLES, model.indices.length, gl.UNSIGNED_SHORT, 0); + + } + + var texture; + + function setImage(imageUrl, callback) { + texture = loadImageFromUrl(gl, imageUrl, function onImageLoad() { + + run(options.animate, callback); + + }); + } + + // setImage(image); + + // asynchronous! + function getImage(format) { + + var img = new Image(); + + img.src = gl.canvas.toDataURL(format || 'image/jpeg'); + + return img; + + } + + // external API: + var distorter = { + options: options, + gl: gl, + lens: lens, + fov: fov, + run: run, + getImage: getImage, + setImage: setImage + } + + return distorter; + +} + +},{"./shaders/fragment3.glfs":128,"./shaders/vertex.glvs":129}],128:[function(require,module,exports){ +module.exports = "#ifdef GL_ES\n\ +precision highp float;\n\ +#endif\n\ +\n\ +uniform vec3 uLensS;\n\ +uniform vec2 uLensF;\n\ +uniform vec2 uFov;\n\ +\n\ +uniform sampler2D uSampler;\n\ +\n\ +varying vec3 vPosition;\n\ +varying vec2 vTextureCoord;\n\ +\n\ +vec2 GLCoord2TextureCoord(vec2 glCoord) {\n\ + return glCoord * vec2(1.0, -1.0)/ 2.0 + vec2(0.5, 0.5);\n\ +}\n\ +\n\ +void main(void){\n\ + float scale = uLensS.z;\n\ + vec3 vPos = vPosition;\n\ + float Fx = uLensF.x;\n\ + float Fy = uLensF.y;\n\ +\n\ + vec2 vMapping = vPos.xy;\n\ + vMapping.x = vMapping.x + ((pow(vPos.y, 2.0)/scale)*vPos.x/scale)*-Fx;\n\ + vMapping.y = vMapping.y + ((pow(vPos.x, 2.0)/scale)*vPos.y/scale)*-Fy;\n\ + vMapping = vMapping * uLensS.xy;\n\ +\n\ + vMapping = GLCoord2TextureCoord(vMapping/scale);\n\ +\n\ + vec4 texture = texture2D(uSampler, vMapping);\n\ + if(vMapping.x > 0.99 || vMapping.x < 0.01 || vMapping.y > 0.99 || vMapping.y < 0.01){\n\ + texture = vec4(0.0, 0.0, 0.0, 1.0);\n\ + }\n\ + gl_FragColor = texture;\n\ +}\n\ +"; + +},{}],129:[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\ +}"; + +},{}],130:[function(require,module,exports){ /* * Display only the green channel */ @@ -35295,7 +35698,7 @@ module.exports = function GreenChannel(options) { } } -},{"../_nomodule/PixelManipulation.js":131}],127:[function(require,module,exports){ +},{"../_nomodule/PixelManipulation.js":135}],131:[function(require,module,exports){ /* * Display only the green channel */ @@ -35333,7 +35736,7 @@ module.exports = function GreenChannel(options) { } } -},{"../_nomodule/PixelManipulation.js":131}],128:[function(require,module,exports){ +},{"../_nomodule/PixelManipulation.js":135}],132:[function(require,module,exports){ /* * NDVI with red filter (blue channel is infrared) */ @@ -35368,7 +35771,7 @@ module.exports = function NdviRed(options) { } } -},{"../_nomodule/PixelManipulation.js":131}],129:[function(require,module,exports){ +},{"../_nomodule/PixelManipulation.js":135}],133:[function(require,module,exports){ module.exports = function SegmentedColormap(options) { options = options || {}; @@ -35402,7 +35805,7 @@ module.exports = function SegmentedColormap(options) { } } -},{"../_nomodule/PixelManipulation.js":131,"./SegmentedColormap":130}],130:[function(require,module,exports){ +},{"../_nomodule/PixelManipulation.js":135,"./SegmentedColormap":134}],134:[function(require,module,exports){ /* * Accepts a normalized ndvi and returns the new color-mapped pixel */ @@ -35461,7 +35864,7 @@ var colormaps = { fastie: fastie_colormap } -},{}],131:[function(require,module,exports){ +},{}],135:[function(require,module,exports){ /* * General purpose per-pixel manipulation * accepting a changePixel() method to remix a pixel's channels diff --git a/src/AddStep.js b/src/AddStep.js index c28dca97..26a8240d 100644 --- a/src/AddStep.js +++ b/src/AddStep.js @@ -9,6 +9,7 @@ function AddStep(ref, image, name, o) { o.selector = o_.selector || 'ismod-' + name; o.container = o_.container || ref.options.selector; o.image = image; + o.inBrowser = ref.options.inBrowser; var module = ref.modules[name].call(ref.images,o); ref.images[image].steps.push(module); diff --git a/src/Modules.js b/src/Modules.js index 3c5df5ba..43934707 100644 --- a/src/Modules.js +++ b/src/Modules.js @@ -8,5 +8,6 @@ module.exports = { 'do-nothing-pix': require('./modules/DoNothingPix/Module.js'), 'invert': require('./modules/Invert/Module'), 'crop': require('./modules/Crop/Module'), - 'segmented-colormap': require('./modules/SegmentedColormap/Module') + 'segmented-colormap': require('./modules/SegmentedColormap/Module'), + 'fisheye-gl': require('./modules/FisheyeGl/Module') } diff --git a/src/modules/FisheyeGl/Module.js b/src/modules/FisheyeGl/Module.js new file mode 100644 index 00000000..3c54aa86 --- /dev/null +++ b/src/modules/FisheyeGl/Module.js @@ -0,0 +1,49 @@ +/* + * Creates Fisheye Effect + */ +module.exports = function DoNothing(options) { + options = options || {}; + options.title = "Fisheye GL"; + var output + + function draw(input,callback) { + this_ = this; + if (!options.inBrowser) { // This module is only for browser + this.output = input; + callback(); + } + else { + + if (!document.querySelector('#image-sequencer-canvas')) { + var canvas = document.createElement('canvas'); + canvas.style.display = "none"; + canvas.setAttribute('id','image-sequencer-canvas'); + document.body.append(canvas); + } + + distorter = require('./fisheyegl.js')({ + selector: "#image-sequencer-canvas" + }); + + distorter.lens.a = options.a || distorter.lens.a; + distorter.lens.b = options.b || distorter.lens.b; + distorter.lens.Fx = options.Fx || distorter.lens.Fx; + distorter.lens.Fy = options.Fy || distorter.lens.Fy; + distorter.lens.scale = options.scale || distorter.lens.scale; + distorter.fov.x = options.x || distorter.fov.x; + distorter.fov.y = options.y || distorter.fov.y; + + distorter.setImage(input.src,function(){ + this_.output = {src: canvas.toDataURL(), format: input.format}; + callback(); + }); + + } + } + + return { + options: options, + draw: draw, + output: output + } +} diff --git a/src/modules/FisheyeGl/fisheyegl.js b/src/modules/FisheyeGl/fisheyegl.js new file mode 100644 index 00000000..312dae34 --- /dev/null +++ b/src/modules/FisheyeGl/fisheyegl.js @@ -0,0 +1,295 @@ +module.exports = function FisheyeGl (options){ + + // Defaults: + options = options || {}; + + options.width = options.width || 800; + options.height = options.height || 600; + + var model = options.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 + ] + }; + + var lens = options.lens || { + a : 1.0, + b : 1.0, + Fx : 0.0, + Fy : 0.0, + scale : 1.5 + }; + var fov = options.fov || { + x : 1.0, + y : 1.0 + } + var image = options.image || "images/barrel-distortion.png"; + + var selector = options.selector || "#canvas"; + var gl = getGLContext(selector); + + var vertexSrc = require("./shaders/vertex.glvs"); + var fragmentSrc = require("./shaders/fragment3.glfs"); + + var program = compileShader(gl, vertexSrc, fragmentSrc) + gl.useProgram(program); + + var aVertexPosition = gl.getAttribLocation(program, "aVertexPosition"); + var aTextureCoord = gl.getAttribLocation(program, "aTextureCoord"); + var uSampler = gl.getUniformLocation(program, "uSampler"); + var uLensS = gl.getUniformLocation(program, "uLensS"); + var uLensF = gl.getUniformLocation(program, "uLensF"); + var uFov = gl.getUniformLocation(program, "uFov"); + + var vertexBuffer, + indexBuffer, + textureBuffer; + + function createBuffers() { + + vertexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(model.vertex), gl.STATIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + + indexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(model.indices), gl.STATIC_DRAW); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + + textureBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(model.textureCoords), gl.STATIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + + } + + createBuffers(); + + function getGLContext(selector){ + var canvas = document.querySelector(selector); + + if(canvas == null){ + throw new Error("there is no canvas on this page"); + } + + var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; + for (var i = 0; i < names.length; ++i) { + var gl; + try { + gl = canvas.getContext(names[i], { preserveDrawingBuffer: true }); + } catch(e) { + continue; + } + if (gl) return gl; + } + + throw new Error("WebGL is not supported!"); + } + + function compileShader(gl, vertexSrc, fragmentSrc){ + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, vertexSrc); + gl.compileShader(vertexShader); + + _checkCompile(vertexShader); + + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragmentShader, fragmentSrc); + gl.compileShader(fragmentShader); + + _checkCompile(fragmentShader); + + var program = gl.createProgram(); + + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + + gl.linkProgram(program); + + return program; + + function _checkCompile(shader){ + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + throw new Error(gl.getShaderInfoLog(shader)); + } + } + } + + function loadFile(url, callback){ + var ajax = new XMLHttpRequest(); + + if(callback) { + ajax.addEventListener("readystatechange", on) + ajax.open("GET", url, true); + ajax.send(null); + } else { + ajax.open("GET", url, false); + ajax.send(null); + + if(ajax.status == 200){ + return ajax.responseText; + } + } + + function on(){ + if(ajax.readyState === 4){ + //complete requset + if(ajax.status === 200){ + //not error + callback(null, ajax.responseText); + } else { + callback(new Error("fail to load!")); + } + } + } + } + + function loadImage(gl, img, callback, texture){ + texture = texture || gl.createTexture(); + + gl.bindTexture(gl.TEXTURE_2D, texture); + + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); //gl.NEAREST is also allowed, instead of gl.LINEAR, as neither mipmap. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); //Prevents s-coordinate wrapping (repeating). + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); //Prevents t-coordinate wrapping (repeating). + //gl.generateMipmap(gl.TEXTURE_2D); + gl.bindTexture(gl.TEXTURE_2D, null); + + if(callback) callback(null, texture); + return texture; + } + + function loadImageFromUrl(gl, url, callback){ + var texture = gl.createTexture(); + var img = new Image(); + img.addEventListener("load", function onload(){ + loadImage(gl, img, callback, texture); + options.width = img.width; + options.height = img.height; + resize( + options.width, + options.height + ) + }); + img.src = url; + return texture; + } + + function run(animate, callback){ + var f = window.requestAnimationFrame || window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; + + // ugh + if(animate === true){ + if(f){ + f(on); + } else { + throw new Error("do not support 'requestAnimationFram'"); + } + } else { + f(on); + } + + var current = null; + function on(t){ + if(!current) current = t; + var dt = t - current; + current = t; + options.runner(dt); + if (callback) callback(); + if (animate === true) f(on); + } + } + + function resize(w, h) { + gl.viewport(0, 0, w, h); + gl.canvas.width = w; + gl.canvas.height = h; + } + + options.runner = options.runner|| function runner(dt){ + + gl.clearColor(0.0, 0.0, 0.0, 1.0); + gl.enable(gl.DEPTH_TEST); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + gl.enableVertexAttribArray(aVertexPosition); + + gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); + gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0); + + gl.enableVertexAttribArray(aTextureCoord); + + gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer); + gl.vertexAttribPointer(aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.uniform1i(uSampler, 0); + + gl.uniform3fv(uLensS, [lens.a, lens.b, lens.scale]); + gl.uniform2fv(uLensF, [lens.Fx, lens.Fy]); + gl.uniform2fv(uFov, [fov.x, fov.y]); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); + gl.drawElements(gl.TRIANGLES, model.indices.length, gl.UNSIGNED_SHORT, 0); + + } + + var texture; + + function setImage(imageUrl, callback) { + texture = loadImageFromUrl(gl, imageUrl, function onImageLoad() { + + run(options.animate, callback); + + }); + } + + // setImage(image); + + // asynchronous! + function getImage(format) { + + var img = new Image(); + + img.src = gl.canvas.toDataURL(format || 'image/jpeg'); + + return img; + + } + + // external API: + var distorter = { + options: options, + gl: gl, + lens: lens, + fov: fov, + run: run, + getImage: getImage, + setImage: setImage + } + + return distorter; + +} diff --git a/src/modules/FisheyeGl/shaders/fragment3.glfs.js b/src/modules/FisheyeGl/shaders/fragment3.glfs.js new file mode 100644 index 00000000..c4dbaa1c --- /dev/null +++ b/src/modules/FisheyeGl/shaders/fragment3.glfs.js @@ -0,0 +1,37 @@ +module.exports = "#ifdef GL_ES\n\ +precision highp float;\n\ +#endif\n\ +\n\ +uniform vec3 uLensS;\n\ +uniform vec2 uLensF;\n\ +uniform vec2 uFov;\n\ +\n\ +uniform sampler2D uSampler;\n\ +\n\ +varying vec3 vPosition;\n\ +varying vec2 vTextureCoord;\n\ +\n\ +vec2 GLCoord2TextureCoord(vec2 glCoord) {\n\ + return glCoord * vec2(1.0, -1.0)/ 2.0 + vec2(0.5, 0.5);\n\ +}\n\ +\n\ +void main(void){\n\ + float scale = uLensS.z;\n\ + vec3 vPos = vPosition;\n\ + float Fx = uLensF.x;\n\ + float Fy = uLensF.y;\n\ +\n\ + vec2 vMapping = vPos.xy;\n\ + vMapping.x = vMapping.x + ((pow(vPos.y, 2.0)/scale)*vPos.x/scale)*-Fx;\n\ + vMapping.y = vMapping.y + ((pow(vPos.x, 2.0)/scale)*vPos.y/scale)*-Fy;\n\ + vMapping = vMapping * uLensS.xy;\n\ +\n\ + vMapping = GLCoord2TextureCoord(vMapping/scale);\n\ +\n\ + vec4 texture = texture2D(uSampler, vMapping);\n\ + if(vMapping.x > 0.99 || vMapping.x < 0.01 || vMapping.y > 0.99 || vMapping.y < 0.01){\n\ + texture = vec4(0.0, 0.0, 0.0, 1.0);\n\ + }\n\ + gl_FragColor = texture;\n\ +}\n\ +"; diff --git a/src/modules/FisheyeGl/shaders/vertex.glvs.js b/src/modules/FisheyeGl/shaders/vertex.glvs.js new file mode 100644 index 00000000..6020db1a --- /dev/null +++ b/src/modules/FisheyeGl/shaders/vertex.glvs.js @@ -0,0 +1,12 @@ +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\ +}";