mirror of https://github.com/vladmandic/human
update imagefx
parent
224f3d26c0
commit
d23fb162a9
|
@ -31,6 +31,9 @@ import jsonView from './helpers/jsonview.js';
|
||||||
let human;
|
let human;
|
||||||
|
|
||||||
let userConfig = {
|
let userConfig = {
|
||||||
|
face: { enabled: false },
|
||||||
|
body: { enabled: false },
|
||||||
|
hand: { enabled: false },
|
||||||
/*
|
/*
|
||||||
warmup: 'none',
|
warmup: 'none',
|
||||||
backend: 'humangl',
|
backend: 'humangl',
|
||||||
|
@ -91,7 +94,7 @@ const ui = {
|
||||||
autoPlay: false, // start webcam & detection on load
|
autoPlay: false, // start webcam & detection on load
|
||||||
|
|
||||||
// internal variables
|
// internal variables
|
||||||
exceptionHandler: true, // should capture all unhandled exceptions
|
exceptionHandler: false, // should capture all unhandled exceptions
|
||||||
busy: false, // internal camera busy flag
|
busy: false, // internal camera busy flag
|
||||||
menuWidth: 0, // internal
|
menuWidth: 0, // internal
|
||||||
menuHeight: 0, // internal
|
menuHeight: 0, // internal
|
||||||
|
@ -147,6 +150,10 @@ let worker;
|
||||||
let bench;
|
let bench;
|
||||||
let lastDetectedResult = {};
|
let lastDetectedResult = {};
|
||||||
|
|
||||||
|
// helper function: async pause
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
// helper function: translates json to human readable string
|
// helper function: translates json to human readable string
|
||||||
function str(...msg) {
|
function str(...msg) {
|
||||||
if (!Array.isArray(msg)) return msg;
|
if (!Array.isArray(msg)) return msg;
|
||||||
|
@ -225,15 +232,18 @@ async function calcSimmilarity(result) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLive = (input) => {
|
const isLive = (input) => {
|
||||||
const videoLive = input.readyState > 2;
|
const isCamera = input.srcObject?.getVideoTracks()[0] && input.srcObject?.getVideoTracks()[0].enabled;
|
||||||
const cameraLive = input.srcObject?.getVideoTracks()[0].readyState === 'live';
|
const isVideoLive = input.readyState > 2;
|
||||||
const live = (videoLive || cameraLive) && (!input.paused);
|
const isCameraLive = input.srcObject?.getVideoTracks()[0].readyState === 'live';
|
||||||
|
let live = isCamera ? isCameraLive : isVideoLive;
|
||||||
|
live = live && !input.paused;
|
||||||
return live;
|
return live;
|
||||||
};
|
};
|
||||||
|
|
||||||
// draws processed results and starts processing of a next frame
|
// draws processed results and starts processing of a next frame
|
||||||
let lastDraw = performance.now();
|
let lastDraw = performance.now();
|
||||||
async function drawResults(input) {
|
async function drawResults(input) {
|
||||||
|
// await delay(25);
|
||||||
const result = lastDetectedResult;
|
const result = lastDetectedResult;
|
||||||
const canvas = document.getElementById('canvas');
|
const canvas = document.getElementById('canvas');
|
||||||
|
|
||||||
|
@ -325,6 +335,7 @@ async function drawResults(input) {
|
||||||
ui.drawThread = requestAnimationFrame(() => drawResults(input));
|
ui.drawThread = requestAnimationFrame(() => drawResults(input));
|
||||||
} else {
|
} else {
|
||||||
cancelAnimationFrame(ui.drawThread);
|
cancelAnimationFrame(ui.drawThread);
|
||||||
|
videoPause();
|
||||||
ui.drawThread = null;
|
ui.drawThread = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -148,7 +148,7 @@ export function process(input: Input, config: Config, getTensor: boolean = true)
|
||||||
if (config.filter.technicolor) fx.addFilter('technicolor');
|
if (config.filter.technicolor) fx.addFilter('technicolor');
|
||||||
if (config.filter.polaroid) fx.addFilter('polaroid');
|
if (config.filter.polaroid) fx.addFilter('polaroid');
|
||||||
if (config.filter.pixelate !== 0) fx.addFilter('pixelate', config.filter.pixelate);
|
if (config.filter.pixelate !== 0) fx.addFilter('pixelate', config.filter.pixelate);
|
||||||
fx.apply(inCanvas);
|
outCanvas = fx.apply(inCanvas);
|
||||||
} else {
|
} else {
|
||||||
copy(inCanvas, outCanvas); // if no filters applied, output canvas is input canvas
|
copy(inCanvas, outCanvas); // if no filters applied, output canvas is input canvas
|
||||||
if (fx) fx = null;
|
if (fx) fx = null;
|
||||||
|
@ -156,6 +156,7 @@ export function process(input: Input, config: Config, getTensor: boolean = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!getTensor) return { tensor: null, canvas: outCanvas }; // just canvas was requested
|
if (!getTensor) return { tensor: null, canvas: outCanvas }; // just canvas was requested
|
||||||
|
if (!outCanvas) throw new Error('cannot create output canvas');
|
||||||
|
|
||||||
// create tensor from image unless input was a tensor already
|
// create tensor from image unless input was a tensor already
|
||||||
let pixels;
|
let pixels;
|
||||||
|
|
|
@ -1,15 +1,34 @@
|
||||||
/**
|
/**
|
||||||
* Image Filters in WebGL algoritm implementation
|
* Image Filters in WebGL algoritm implementation
|
||||||
*
|
|
||||||
* Based on: [WebGLImageFilter](https://github.com/phoboslab/WebGLImageFilter)
|
* Based on: [WebGLImageFilter](https://github.com/phoboslab/WebGLImageFilter)
|
||||||
*
|
|
||||||
* This module is written in ES5 JS and does not conform to code and style standards
|
* This module is written in ES5 JS and does not conform to code and style standards
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// @ts-nocheck
|
import * as shaders from './imagefxshaders';
|
||||||
|
|
||||||
function GLProgram(gl, vertexSource, fragmentSource) {
|
class GLProgram {
|
||||||
const _collect = function (source, prefix, collection) {
|
uniform = {};
|
||||||
|
attribute = {};
|
||||||
|
gl: WebGLRenderingContext;
|
||||||
|
id: WebGLProgram;
|
||||||
|
constructor(gl, vertexSource, fragmentSource) {
|
||||||
|
this.gl = gl;
|
||||||
|
const _vsh = this.compile(vertexSource, this.gl.VERTEX_SHADER);
|
||||||
|
const _fsh = this.compile(fragmentSource, this.gl.FRAGMENT_SHADER);
|
||||||
|
this.id = this.gl.createProgram() as WebGLProgram;
|
||||||
|
this.gl.attachShader(this.id, _vsh);
|
||||||
|
this.gl.attachShader(this.id, _fsh);
|
||||||
|
this.gl.linkProgram(this.id);
|
||||||
|
if (!this.gl.getProgramParameter(this.id, this.gl.LINK_STATUS)) throw new Error(`filter: gl link failed: ${this.gl.getProgramInfoLog(this.id)}`);
|
||||||
|
this.gl.useProgram(this.id);
|
||||||
|
this.collect(vertexSource, 'attribute', this.attribute); // Collect attributes
|
||||||
|
for (const a in this.attribute) this.attribute[a] = this.gl.getAttribLocation(this.id, a);
|
||||||
|
this.collect(vertexSource, 'uniform', this.uniform); // Collect uniforms
|
||||||
|
this.collect(fragmentSource, 'uniform', this.uniform);
|
||||||
|
for (const u in this.uniform) this.uniform[u] = this.gl.getUniformLocation(this.id, u);
|
||||||
|
}
|
||||||
|
|
||||||
|
collect = (source, prefix, collection) => {
|
||||||
const r = new RegExp('\\b' + prefix + ' \\w+ (\\w+)', 'ig');
|
const r = new RegExp('\\b' + prefix + ' \\w+ (\\w+)', 'ig');
|
||||||
source.replace(r, (match, name) => {
|
source.replace(r, (match, name) => {
|
||||||
collection[name] = 0;
|
collection[name] = 0;
|
||||||
|
@ -17,55 +36,32 @@ function GLProgram(gl, vertexSource, fragmentSource) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const _compile = function (source, type) {
|
compile = (source, type): WebGLShader => {
|
||||||
const shader = gl.createShader(type);
|
const shader = this.gl.createShader(type) as WebGLShader;
|
||||||
gl.shaderSource(shader, source);
|
this.gl.shaderSource(shader, source);
|
||||||
gl.compileShader(shader);
|
this.gl.compileShader(shader);
|
||||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) throw new Error('filter: gl compile failed', gl.getShaderInfoLog(shader));
|
if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) throw new Error(`filter: gl compile failed: ${this.gl.getShaderInfoLog(shader)}`);
|
||||||
return shader;
|
return shader;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.uniform = {};
|
|
||||||
this.attribute = {};
|
|
||||||
const _vsh = _compile(vertexSource, gl.VERTEX_SHADER);
|
|
||||||
const _fsh = _compile(fragmentSource, gl.FRAGMENT_SHADER);
|
|
||||||
this.id = gl.createProgram();
|
|
||||||
gl.attachShader(this.id, _vsh);
|
|
||||||
gl.attachShader(this.id, _fsh);
|
|
||||||
gl.linkProgram(this.id);
|
|
||||||
|
|
||||||
if (!gl.getProgramParameter(this.id, gl.LINK_STATUS)) throw new Error('filter: gl link failed', gl.getProgramInfoLog(this.id));
|
|
||||||
|
|
||||||
gl.useProgram(this.id);
|
|
||||||
// Collect attributes
|
|
||||||
_collect(vertexSource, 'attribute', this.attribute);
|
|
||||||
for (const a in this.attribute) this.attribute[a] = gl.getAttribLocation(this.id, a);
|
|
||||||
// Collect uniforms
|
|
||||||
_collect(vertexSource, 'uniform', this.uniform);
|
|
||||||
_collect(fragmentSource, 'uniform', this.uniform);
|
|
||||||
for (const u in this.uniform) this.uniform[u] = gl.getUniformLocation(this.id, u);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// export const GLImageFilter = function (params) {
|
|
||||||
export function GLImageFilter(params) {
|
export function GLImageFilter(params) {
|
||||||
if (!params) params = { };
|
if (!params) params = { };
|
||||||
let _drawCount = 0;
|
let _drawCount = 0;
|
||||||
let _sourceTexture = null;
|
let _sourceTexture = null;
|
||||||
let _lastInChain = false;
|
let _lastInChain = false;
|
||||||
let _currentFramebufferIndex = -1;
|
let _currentFramebufferIndex = -1;
|
||||||
let _tempFramebuffers = [null, null];
|
let _tempFramebuffers: [null, null] | [{ fbo: any, texture: any }] = [null, null];
|
||||||
let _filterChain = [];
|
let _filterChain: Record<string, unknown>[] = [];
|
||||||
let _width = -1;
|
let _width = -1;
|
||||||
let _height = -1;
|
let _height = -1;
|
||||||
let _vertexBuffer = null;
|
let _vertexBuffer = null;
|
||||||
let _currentProgram = null;
|
let _currentProgram: GLProgram | null = null;
|
||||||
const _filter = {};
|
const _canvas = params.canvas || typeof OffscreenCanvas !== 'undefined' ? new OffscreenCanvas(100, 100) : document.createElement('canvas');
|
||||||
const _canvas = params.canvas || document.createElement('canvas');
|
const _shaderProgramCache = { }; // key is the shader program source, value is the compiled program
|
||||||
// key is the shader program source, value is the compiled program
|
|
||||||
const _shaderProgramCache = { };
|
|
||||||
const DRAW = { INTERMEDIATE: 1 };
|
const DRAW = { INTERMEDIATE: 1 };
|
||||||
const gl = _canvas.getContext('webgl');
|
const gl = _canvas.getContext('webgl');
|
||||||
if (!gl) throw new Error('filter: context failed');
|
if (!gl) throw new Error('filter: cannot get webgl context');
|
||||||
|
|
||||||
this.addFilter = function (name) {
|
this.addFilter = function (name) {
|
||||||
// eslint-disable-next-line prefer-rest-params
|
// eslint-disable-next-line prefer-rest-params
|
||||||
|
@ -79,27 +75,20 @@ export function GLImageFilter(params) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const _resize = function (width, height) {
|
const _resize = function (width, height) {
|
||||||
// Same width/height? Nothing to do here
|
if (width === _width && height === _height) return; // Same width/height? Nothing to do here
|
||||||
if (width === _width && height === _height) { return; }
|
|
||||||
_canvas.width = width;
|
_canvas.width = width;
|
||||||
_width = width;
|
_width = width;
|
||||||
_canvas.height = height;
|
_canvas.height = height;
|
||||||
_height = height;
|
_height = height;
|
||||||
// Create the context if we don't have it yet
|
if (!_vertexBuffer) { // Create the context if we don't have it yet
|
||||||
if (!_vertexBuffer) {
|
const vertices = new Float32Array([-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0, -1, 1, 0, 0, 1, -1, 1, 1, 1, 1, 1, 0]); // Create the vertex buffer for the two triangles [x, y, u, v] * 6
|
||||||
// Create the vertex buffer for the two triangles [x, y, u, v] * 6
|
|
||||||
const vertices = new Float32Array([
|
|
||||||
-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0,
|
|
||||||
-1, 1, 0, 0, 1, -1, 1, 1, 1, 1, 1, 0,
|
|
||||||
]);
|
|
||||||
// eslint-disable-next-line no-unused-expressions
|
// eslint-disable-next-line no-unused-expressions
|
||||||
(_vertexBuffer = gl.createBuffer(), gl.bindBuffer(gl.ARRAY_BUFFER, _vertexBuffer));
|
(_vertexBuffer = gl.createBuffer(), gl.bindBuffer(gl.ARRAY_BUFFER, _vertexBuffer));
|
||||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||||
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
|
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
|
||||||
}
|
}
|
||||||
gl.viewport(0, 0, _width, _height);
|
gl.viewport(0, 0, _width, _height);
|
||||||
// Delete old temp framebuffers
|
_tempFramebuffers = [null, null]; // Delete old temp framebuffers
|
||||||
_tempFramebuffers = [null, null];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const _createFramebufferTexture = function (width, height) {
|
const _createFramebufferTexture = function (width, height) {
|
||||||
|
@ -125,58 +114,45 @@ export function GLImageFilter(params) {
|
||||||
return _tempFramebuffers[index];
|
return _tempFramebuffers[index];
|
||||||
};
|
};
|
||||||
|
|
||||||
const _draw = function (flags = null) {
|
const _draw = function (flags = 0) {
|
||||||
|
if (!_currentProgram) return;
|
||||||
let source = null;
|
let source = null;
|
||||||
let target = null;
|
let target = null;
|
||||||
let flipY = false;
|
let flipY = false;
|
||||||
// Set up the source
|
if (_drawCount === 0) source = _sourceTexture; // First draw call - use the source texture
|
||||||
if (_drawCount === 0) {
|
else source = _getTempFramebuffer(_currentFramebufferIndex)?.texture; // All following draw calls use the temp buffer last drawn to
|
||||||
// First draw call - use the source texture
|
|
||||||
source = _sourceTexture;
|
|
||||||
} else {
|
|
||||||
// All following draw calls use the temp buffer last drawn to
|
|
||||||
source = _getTempFramebuffer(_currentFramebufferIndex)?.texture;
|
|
||||||
}
|
|
||||||
_drawCount++;
|
_drawCount++;
|
||||||
// Set up the target
|
if (_lastInChain && !(flags & DRAW.INTERMEDIATE)) { // Last filter in our chain - draw directly to the WebGL Canvas. We may also have to flip the image vertically now
|
||||||
if (_lastInChain && !(flags & DRAW.INTERMEDIATE)) {
|
|
||||||
// Last filter in our chain - draw directly to the WebGL Canvas. We may
|
|
||||||
// also have to flip the image vertically now
|
|
||||||
target = null;
|
target = null;
|
||||||
flipY = _drawCount % 2 === 0;
|
flipY = _drawCount % 2 === 0;
|
||||||
} else {
|
} else {
|
||||||
// Intermediate draw call - get a temp buffer to draw to
|
|
||||||
_currentFramebufferIndex = (_currentFramebufferIndex + 1) % 2;
|
_currentFramebufferIndex = (_currentFramebufferIndex + 1) % 2;
|
||||||
target = _getTempFramebuffer(_currentFramebufferIndex)?.fbo;
|
target = _getTempFramebuffer(_currentFramebufferIndex)?.fbo; // Intermediate draw call - get a temp buffer to draw to
|
||||||
}
|
}
|
||||||
// Bind the source and target and draw the two triangles
|
gl.bindTexture(gl.TEXTURE_2D, source); // Bind the source and target and draw the two triangles
|
||||||
gl.bindTexture(gl.TEXTURE_2D, source);
|
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, target);
|
gl.bindFramebuffer(gl.FRAMEBUFFER, target);
|
||||||
gl.uniform1f(_currentProgram.uniform.flipY, (flipY ? -1 : 1));
|
gl.uniform1f(_currentProgram.uniform['flipY'], (flipY ? -1 : 1));
|
||||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.apply = function (image) {
|
this.apply = function (image) {
|
||||||
_resize(image.width, image.height);
|
_resize(image.width, image.height);
|
||||||
_drawCount = 0;
|
_drawCount = 0;
|
||||||
// Create the texture for the input image if we haven't yet
|
if (!_sourceTexture) _sourceTexture = gl.createTexture(); // Create the texture for the input image if we haven't yet
|
||||||
if (!_sourceTexture) _sourceTexture = gl.createTexture();
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, _sourceTexture);
|
gl.bindTexture(gl.TEXTURE_2D, _sourceTexture);
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
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_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||||
// No filters? Just draw
|
if (_filterChain.length === 0) { // draw when done with filters
|
||||||
if (_filterChain.length === 0) {
|
|
||||||
// const program = _compileShader(SHADER.FRAGMENT_IDENTITY);
|
|
||||||
_draw();
|
_draw();
|
||||||
return _canvas;
|
} else { // apply filters one-by-one recursively
|
||||||
}
|
for (let i = 0; i < _filterChain.length; i++) {
|
||||||
for (let i = 0; i < _filterChain.length; i++) {
|
_lastInChain = (i === _filterChain.length - 1);
|
||||||
_lastInChain = (i === _filterChain.length - 1);
|
const f = _filterChain[i];
|
||||||
const f = _filterChain[i];
|
f.func.apply(this, f.args || []);
|
||||||
f.func.apply(this, f.args || []);
|
}
|
||||||
}
|
}
|
||||||
return _canvas;
|
return _canvas;
|
||||||
};
|
};
|
||||||
|
@ -184,355 +160,236 @@ export function GLImageFilter(params) {
|
||||||
const _compileShader = function (fragmentSource) {
|
const _compileShader = function (fragmentSource) {
|
||||||
if (_shaderProgramCache[fragmentSource]) {
|
if (_shaderProgramCache[fragmentSource]) {
|
||||||
_currentProgram = _shaderProgramCache[fragmentSource];
|
_currentProgram = _shaderProgramCache[fragmentSource];
|
||||||
gl.useProgram(_currentProgram.id);
|
gl.useProgram(_currentProgram?.id);
|
||||||
return _currentProgram;
|
return _currentProgram;
|
||||||
}
|
}
|
||||||
// Compile shaders
|
_currentProgram = new GLProgram(gl, shaders.vertexIdentity, fragmentSource);
|
||||||
const SHADER = {};
|
|
||||||
SHADER.VERTEX_IDENTITY = [
|
|
||||||
'precision highp float;',
|
|
||||||
'attribute vec2 pos;',
|
|
||||||
'attribute vec2 uv;',
|
|
||||||
'varying vec2 vUv;',
|
|
||||||
'uniform float flipY;',
|
|
||||||
'void main(void) {',
|
|
||||||
'vUv = uv;',
|
|
||||||
'gl_Position = vec4(pos.x, pos.y*flipY, 0.0, 1.);',
|
|
||||||
'}',
|
|
||||||
].join('\n');
|
|
||||||
SHADER.FRAGMENT_IDENTITY = [
|
|
||||||
'precision highp float;',
|
|
||||||
'varying vec2 vUv;',
|
|
||||||
'uniform sampler2D texture;',
|
|
||||||
'void main(void) {',
|
|
||||||
'gl_FragColor = texture2D(texture, vUv);',
|
|
||||||
'}',
|
|
||||||
].join('\n');
|
|
||||||
_currentProgram = new GLProgram(gl, SHADER.VERTEX_IDENTITY, fragmentSource);
|
|
||||||
const floatSize = Float32Array.BYTES_PER_ELEMENT;
|
const floatSize = Float32Array.BYTES_PER_ELEMENT;
|
||||||
const vertSize = 4 * floatSize;
|
const vertSize = 4 * floatSize;
|
||||||
gl.enableVertexAttribArray(_currentProgram.attribute.pos);
|
gl.enableVertexAttribArray(_currentProgram.attribute['pos']);
|
||||||
gl.vertexAttribPointer(_currentProgram.attribute.pos, 2, gl.FLOAT, false, vertSize, 0 * floatSize);
|
gl.vertexAttribPointer(_currentProgram.attribute['pos'], 2, gl.FLOAT, false, vertSize, 0 * floatSize);
|
||||||
gl.enableVertexAttribArray(_currentProgram.attribute.uv);
|
gl.enableVertexAttribArray(_currentProgram.attribute.uv);
|
||||||
gl.vertexAttribPointer(_currentProgram.attribute.uv, 2, gl.FLOAT, false, vertSize, 2 * floatSize);
|
gl.vertexAttribPointer(_currentProgram.attribute['uv'], 2, gl.FLOAT, false, vertSize, 2 * floatSize);
|
||||||
_shaderProgramCache[fragmentSource] = _currentProgram;
|
_shaderProgramCache[fragmentSource] = _currentProgram;
|
||||||
return _currentProgram;
|
return _currentProgram;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// Color Matrix Filter: Used by most color filters
|
||||||
// Color Matrix Filter
|
const _filter = {
|
||||||
_filter.colorMatrix = function (matrix) {
|
colorMatrix: (matrix) => {
|
||||||
// Create a Float32 Array and normalize the offset component to 0-1
|
const m = new Float32Array(matrix); // Create a Float32 Array and normalize the offset component to 0-1
|
||||||
const m = new Float32Array(matrix);
|
m[4] /= 255;
|
||||||
m[4] /= 255;
|
m[9] /= 255;
|
||||||
m[9] /= 255;
|
m[14] /= 255;
|
||||||
m[14] /= 255;
|
m[19] /= 255;
|
||||||
m[19] /= 255;
|
const shader = (m[18] === 1 && m[3] === 0 && m[8] === 0 && m[13] === 0 && m[15] === 0 && m[16] === 0 && m[17] === 0 && m[19] === 0) // Can we ignore the alpha value? Makes things a bit faster.
|
||||||
// Can we ignore the alpha value? Makes things a bit faster.
|
? shaders.colorMatrixWithoutAlpha
|
||||||
const shader = (m[18] === 1 && m[3] === 0 && m[8] === 0 && m[13] === 0 && m[15] === 0 && m[16] === 0 && m[17] === 0 && m[19] === 0)
|
: shaders.colorMatrixWithAlpha;
|
||||||
? _filter.colorMatrix.SHADER.WITHOUT_ALPHA
|
const program = _compileShader(shader);
|
||||||
: _filter.colorMatrix.SHADER.WITH_ALPHA;
|
gl.uniform1fv(program?.uniform['m'], m);
|
||||||
const program = _compileShader(shader);
|
_draw();
|
||||||
gl.uniform1fv(program.uniform.m, m);
|
},
|
||||||
_draw();
|
|
||||||
|
brightness: (brightness) => {
|
||||||
|
const b = (brightness || 0) + 1;
|
||||||
|
_filter.colorMatrix([
|
||||||
|
b, 0, 0, 0, 0,
|
||||||
|
0, b, 0, 0, 0,
|
||||||
|
0, 0, b, 0, 0,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
saturation: (amount) => {
|
||||||
|
const x = (amount || 0) * 2 / 3 + 1;
|
||||||
|
const y = ((x - 1) * -0.5);
|
||||||
|
_filter.colorMatrix([
|
||||||
|
x, y, y, 0, 0,
|
||||||
|
y, x, y, 0, 0,
|
||||||
|
y, y, x, 0, 0,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
desaturate: () => {
|
||||||
|
_filter.saturation(-1);
|
||||||
|
},
|
||||||
|
|
||||||
|
contrast: (amount) => {
|
||||||
|
const v = (amount || 0) + 1;
|
||||||
|
const o = -128 * (v - 1);
|
||||||
|
_filter.colorMatrix([
|
||||||
|
v, 0, 0, 0, o,
|
||||||
|
0, v, 0, 0, o,
|
||||||
|
0, 0, v, 0, o,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
negative: () => {
|
||||||
|
_filter.contrast(-2);
|
||||||
|
},
|
||||||
|
|
||||||
|
hue: (rotation) => {
|
||||||
|
rotation = (rotation || 0) / 180 * Math.PI;
|
||||||
|
const cos = Math.cos(rotation);
|
||||||
|
const sin = Math.sin(rotation);
|
||||||
|
const lumR = 0.213;
|
||||||
|
const lumG = 0.715;
|
||||||
|
const lumB = 0.072;
|
||||||
|
_filter.colorMatrix([
|
||||||
|
lumR + cos * (1 - lumR) + sin * (-lumR), lumG + cos * (-lumG) + sin * (-lumG), lumB + cos * (-lumB) + sin * (1 - lumB), 0, 0,
|
||||||
|
lumR + cos * (-lumR) + sin * (0.143), lumG + cos * (1 - lumG) + sin * (0.140), lumB + cos * (-lumB) + sin * (-0.283), 0, 0,
|
||||||
|
lumR + cos * (-lumR) + sin * (-(1 - lumR)), lumG + cos * (-lumG) + sin * (lumG), lumB + cos * (1 - lumB) + sin * (lumB), 0, 0,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
desaturateLuminance: () => {
|
||||||
|
_filter.colorMatrix([
|
||||||
|
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
|
||||||
|
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
|
||||||
|
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
sepia: () => {
|
||||||
|
_filter.colorMatrix([
|
||||||
|
0.393, 0.7689999, 0.18899999, 0, 0,
|
||||||
|
0.349, 0.6859999, 0.16799999, 0, 0,
|
||||||
|
0.272, 0.5339999, 0.13099999, 0, 0,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
brownie: () => {
|
||||||
|
_filter.colorMatrix([
|
||||||
|
0.5997023498159715, 0.34553243048391263, -0.2708298674538042, 0, 47.43192855600873,
|
||||||
|
-0.037703249837783157, 0.8609577587992641, 0.15059552388459913, 0, -36.96841498319127,
|
||||||
|
0.24113635128153335, -0.07441037908422492, 0.44972182064877153, 0, -7.562075277591283,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
vintagePinhole: () => {
|
||||||
|
_filter.colorMatrix([
|
||||||
|
0.6279345635605994, 0.3202183420819367, -0.03965408211312453, 0, 9.651285835294123,
|
||||||
|
0.02578397704808868, 0.6441188644374771, 0.03259127616149294, 0, 7.462829176470591,
|
||||||
|
0.0466055556782719, -0.0851232987247891, 0.5241648018700465, 0, 5.159190588235296,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
kodachrome: () => {
|
||||||
|
_filter.colorMatrix([
|
||||||
|
1.1285582396593525, -0.3967382283601348, -0.03992559172921793, 0, 63.72958762196502,
|
||||||
|
-0.16404339962244616, 1.0835251566291304, -0.05498805115633132, 0, 24.732407896706203,
|
||||||
|
-0.16786010706155763, -0.5603416277695248, 1.6014850761964943, 0, 35.62982807460946,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
technicolor: () => {
|
||||||
|
_filter.colorMatrix([
|
||||||
|
1.9125277891456083, -0.8545344976951645, -0.09155508482755585, 0, 11.793603434377337,
|
||||||
|
-0.3087833385928097, 1.7658908555458428, -0.10601743074722245, 0, -70.35205161461398,
|
||||||
|
-0.231103377548616, -0.7501899197440212, 1.847597816108189, 0, 30.950940869491138,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
polaroid: () => {
|
||||||
|
_filter.colorMatrix([
|
||||||
|
1.438, -0.062, -0.062, 0, 0,
|
||||||
|
-0.122, 1.378, -0.122, 0, 0,
|
||||||
|
-0.016, -0.016, 1.483, 0, 0,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
shiftToBGR: () => {
|
||||||
|
_filter.colorMatrix([
|
||||||
|
0, 0, 1, 0, 0,
|
||||||
|
0, 1, 0, 0, 0,
|
||||||
|
1, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convolution Filter
|
||||||
|
convolution: (matrix) => {
|
||||||
|
const m = new Float32Array(matrix);
|
||||||
|
const pixelSizeX = 1 / _width;
|
||||||
|
const pixelSizeY = 1 / _height;
|
||||||
|
const program = _compileShader(shaders.convolution);
|
||||||
|
gl.uniform1fv(program?.uniform['m'], m);
|
||||||
|
gl.uniform2f(program?.uniform['px'], pixelSizeX, pixelSizeY);
|
||||||
|
_draw();
|
||||||
|
},
|
||||||
|
|
||||||
|
detectEdges: () => {
|
||||||
|
_filter.convolution.call(this, [
|
||||||
|
0, 1, 0,
|
||||||
|
1, -4, 1,
|
||||||
|
0, 1, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
sobelX: () => {
|
||||||
|
_filter.convolution.call(this, [
|
||||||
|
-1, 0, 1,
|
||||||
|
-2, 0, 2,
|
||||||
|
-1, 0, 1,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
sobelY: () => {
|
||||||
|
_filter.convolution.call(this, [
|
||||||
|
-1, -2, -1,
|
||||||
|
0, 0, 0,
|
||||||
|
1, 2, 1,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
sharpen: (amount) => {
|
||||||
|
const a = amount || 1;
|
||||||
|
_filter.convolution.call(this, [
|
||||||
|
0, -1 * a, 0,
|
||||||
|
-1 * a, 1 + 4 * a, -1 * a,
|
||||||
|
0, -1 * a, 0,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
emboss: (size) => {
|
||||||
|
const s = size || 1;
|
||||||
|
_filter.convolution.call(this, [
|
||||||
|
-2 * s, -1 * s, 0,
|
||||||
|
-1 * s, 1, 1 * s,
|
||||||
|
0, 1 * s, 2 * s,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Blur Filter
|
||||||
|
blur: (size) => {
|
||||||
|
const blurSizeX = (size / 7) / _width;
|
||||||
|
const blurSizeY = (size / 7) / _height;
|
||||||
|
const program = _compileShader(shaders.blur);
|
||||||
|
// Vertical
|
||||||
|
gl.uniform2f(program?.uniform['px'], 0, blurSizeY);
|
||||||
|
_draw(DRAW.INTERMEDIATE);
|
||||||
|
// Horizontal
|
||||||
|
gl.uniform2f(program?.uniform['px'], blurSizeX, 0);
|
||||||
|
_draw();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Pixelate Filter
|
||||||
|
pixelate: (size) => {
|
||||||
|
const blurSizeX = (size) / _width;
|
||||||
|
const blurSizeY = (size) / _height;
|
||||||
|
const program = _compileShader(shaders.pixelate);
|
||||||
|
gl.uniform2f(program?.uniform['size'], blurSizeX, blurSizeY);
|
||||||
|
_draw();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
_filter.colorMatrix.SHADER = {};
|
|
||||||
_filter.colorMatrix.SHADER.WITH_ALPHA = [
|
|
||||||
'precision highp float;',
|
|
||||||
'varying vec2 vUv;',
|
|
||||||
'uniform sampler2D texture;',
|
|
||||||
'uniform float m[20];',
|
|
||||||
'void main(void) {',
|
|
||||||
'vec4 c = texture2D(texture, vUv);',
|
|
||||||
'gl_FragColor.r = m[0] * c.r + m[1] * c.g + m[2] * c.b + m[3] * c.a + m[4];',
|
|
||||||
'gl_FragColor.g = m[5] * c.r + m[6] * c.g + m[7] * c.b + m[8] * c.a + m[9];',
|
|
||||||
'gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[13] * c.a + m[14];',
|
|
||||||
'gl_FragColor.a = m[15] * c.r + m[16] * c.g + m[17] * c.b + m[18] * c.a + m[19];',
|
|
||||||
'}',
|
|
||||||
].join('\n');
|
|
||||||
_filter.colorMatrix.SHADER.WITHOUT_ALPHA = [
|
|
||||||
'precision highp float;',
|
|
||||||
'varying vec2 vUv;',
|
|
||||||
'uniform sampler2D texture;',
|
|
||||||
'uniform float m[20];',
|
|
||||||
'void main(void) {',
|
|
||||||
'vec4 c = texture2D(texture, vUv);',
|
|
||||||
'gl_FragColor.r = m[0] * c.r + m[1] * c.g + m[2] * c.b + m[4];',
|
|
||||||
'gl_FragColor.g = m[5] * c.r + m[6] * c.g + m[7] * c.b + m[9];',
|
|
||||||
'gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[14];',
|
|
||||||
'gl_FragColor.a = c.a;',
|
|
||||||
'}',
|
|
||||||
].join('\n');
|
|
||||||
|
|
||||||
_filter.brightness = function (brightness) {
|
|
||||||
const b = (brightness || 0) + 1;
|
|
||||||
_filter.colorMatrix([
|
|
||||||
b, 0, 0, 0, 0,
|
|
||||||
0, b, 0, 0, 0,
|
|
||||||
0, 0, b, 0, 0,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.saturation = function (amount) {
|
|
||||||
const x = (amount || 0) * 2 / 3 + 1;
|
|
||||||
const y = ((x - 1) * -0.5);
|
|
||||||
_filter.colorMatrix([
|
|
||||||
x, y, y, 0, 0,
|
|
||||||
y, x, y, 0, 0,
|
|
||||||
y, y, x, 0, 0,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.desaturate = function () {
|
|
||||||
_filter.saturation(-1);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.contrast = function (amount) {
|
|
||||||
const v = (amount || 0) + 1;
|
|
||||||
const o = -128 * (v - 1);
|
|
||||||
|
|
||||||
_filter.colorMatrix([
|
|
||||||
v, 0, 0, 0, o,
|
|
||||||
0, v, 0, 0, o,
|
|
||||||
0, 0, v, 0, o,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.negative = function () {
|
|
||||||
_filter.contrast(-2);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.hue = function (rotation) {
|
|
||||||
rotation = (rotation || 0) / 180 * Math.PI;
|
|
||||||
const cos = Math.cos(rotation);
|
|
||||||
const sin = Math.sin(rotation);
|
|
||||||
const lumR = 0.213;
|
|
||||||
const lumG = 0.715;
|
|
||||||
const lumB = 0.072;
|
|
||||||
|
|
||||||
_filter.colorMatrix([
|
|
||||||
lumR + cos * (1 - lumR) + sin * (-lumR), lumG + cos * (-lumG) + sin * (-lumG), lumB + cos * (-lumB) + sin * (1 - lumB), 0, 0,
|
|
||||||
lumR + cos * (-lumR) + sin * (0.143), lumG + cos * (1 - lumG) + sin * (0.140), lumB + cos * (-lumB) + sin * (-0.283), 0, 0,
|
|
||||||
lumR + cos * (-lumR) + sin * (-(1 - lumR)), lumG + cos * (-lumG) + sin * (lumG), lumB + cos * (1 - lumB) + sin * (lumB), 0, 0,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.desaturateLuminance = function () {
|
|
||||||
_filter.colorMatrix([
|
|
||||||
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
|
|
||||||
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
|
|
||||||
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.sepia = function () {
|
|
||||||
_filter.colorMatrix([
|
|
||||||
0.393, 0.7689999, 0.18899999, 0, 0,
|
|
||||||
0.349, 0.6859999, 0.16799999, 0, 0,
|
|
||||||
0.272, 0.5339999, 0.13099999, 0, 0,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.brownie = function () {
|
|
||||||
_filter.colorMatrix([
|
|
||||||
0.5997023498159715, 0.34553243048391263, -0.2708298674538042, 0, 47.43192855600873,
|
|
||||||
-0.037703249837783157, 0.8609577587992641, 0.15059552388459913, 0, -36.96841498319127,
|
|
||||||
0.24113635128153335, -0.07441037908422492, 0.44972182064877153, 0, -7.562075277591283,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.vintagePinhole = function () {
|
|
||||||
_filter.colorMatrix([
|
|
||||||
0.6279345635605994, 0.3202183420819367, -0.03965408211312453, 0, 9.651285835294123,
|
|
||||||
0.02578397704808868, 0.6441188644374771, 0.03259127616149294, 0, 7.462829176470591,
|
|
||||||
0.0466055556782719, -0.0851232987247891, 0.5241648018700465, 0, 5.159190588235296,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.kodachrome = function () {
|
|
||||||
_filter.colorMatrix([
|
|
||||||
1.1285582396593525, -0.3967382283601348, -0.03992559172921793, 0, 63.72958762196502,
|
|
||||||
-0.16404339962244616, 1.0835251566291304, -0.05498805115633132, 0, 24.732407896706203,
|
|
||||||
-0.16786010706155763, -0.5603416277695248, 1.6014850761964943, 0, 35.62982807460946,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.technicolor = function () {
|
|
||||||
_filter.colorMatrix([
|
|
||||||
1.9125277891456083, -0.8545344976951645, -0.09155508482755585, 0, 11.793603434377337,
|
|
||||||
-0.3087833385928097, 1.7658908555458428, -0.10601743074722245, 0, -70.35205161461398,
|
|
||||||
-0.231103377548616, -0.7501899197440212, 1.847597816108189, 0, 30.950940869491138,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.polaroid = function () {
|
|
||||||
_filter.colorMatrix([
|
|
||||||
1.438, -0.062, -0.062, 0, 0,
|
|
||||||
-0.122, 1.378, -0.122, 0, 0,
|
|
||||||
-0.016, -0.016, 1.483, 0, 0,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.shiftToBGR = function () {
|
|
||||||
_filter.colorMatrix([
|
|
||||||
0, 0, 1, 0, 0,
|
|
||||||
0, 1, 0, 0, 0,
|
|
||||||
1, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Convolution Filter
|
|
||||||
_filter.convolution = function (matrix) {
|
|
||||||
const m = new Float32Array(matrix);
|
|
||||||
const pixelSizeX = 1 / _width;
|
|
||||||
const pixelSizeY = 1 / _height;
|
|
||||||
const program = _compileShader(_filter.convolution.SHADER);
|
|
||||||
gl.uniform1fv(program.uniform.m, m);
|
|
||||||
gl.uniform2f(program.uniform.px, pixelSizeX, pixelSizeY);
|
|
||||||
_draw();
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.convolution.SHADER = [
|
|
||||||
'precision highp float;',
|
|
||||||
'varying vec2 vUv;',
|
|
||||||
'uniform sampler2D texture;',
|
|
||||||
'uniform vec2 px;',
|
|
||||||
'uniform float m[9];',
|
|
||||||
'void main(void) {',
|
|
||||||
'vec4 c11 = texture2D(texture, vUv - px);', // top left
|
|
||||||
'vec4 c12 = texture2D(texture, vec2(vUv.x, vUv.y - px.y));', // top center
|
|
||||||
'vec4 c13 = texture2D(texture, vec2(vUv.x + px.x, vUv.y - px.y));', // top right
|
|
||||||
'vec4 c21 = texture2D(texture, vec2(vUv.x - px.x, vUv.y) );', // mid left
|
|
||||||
'vec4 c22 = texture2D(texture, vUv);', // mid center
|
|
||||||
'vec4 c23 = texture2D(texture, vec2(vUv.x + px.x, vUv.y) );', // mid right
|
|
||||||
'vec4 c31 = texture2D(texture, vec2(vUv.x - px.x, vUv.y + px.y) );', // bottom left
|
|
||||||
'vec4 c32 = texture2D(texture, vec2(vUv.x, vUv.y + px.y) );', // bottom center
|
|
||||||
'vec4 c33 = texture2D(texture, vUv + px );', // bottom right
|
|
||||||
'gl_FragColor = ',
|
|
||||||
'c11 * m[0] + c12 * m[1] + c22 * m[2] +',
|
|
||||||
'c21 * m[3] + c22 * m[4] + c23 * m[5] +',
|
|
||||||
'c31 * m[6] + c32 * m[7] + c33 * m[8];',
|
|
||||||
'gl_FragColor.a = c22.a;',
|
|
||||||
'}',
|
|
||||||
].join('\n');
|
|
||||||
|
|
||||||
_filter.detectEdges = function () {
|
|
||||||
_filter.convolution.call(this, [
|
|
||||||
0, 1, 0,
|
|
||||||
1, -4, 1,
|
|
||||||
0, 1, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.sobelX = function () {
|
|
||||||
_filter.convolution.call(this, [
|
|
||||||
-1, 0, 1,
|
|
||||||
-2, 0, 2,
|
|
||||||
-1, 0, 1,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.sobelY = function () {
|
|
||||||
_filter.convolution.call(this, [
|
|
||||||
-1, -2, -1,
|
|
||||||
0, 0, 0,
|
|
||||||
1, 2, 1,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.sharpen = function (amount) {
|
|
||||||
const a = amount || 1;
|
|
||||||
_filter.convolution.call(this, [
|
|
||||||
0, -1 * a, 0,
|
|
||||||
-1 * a, 1 + 4 * a, -1 * a,
|
|
||||||
0, -1 * a, 0,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.emboss = function (size) {
|
|
||||||
const s = size || 1;
|
|
||||||
_filter.convolution.call(this, [
|
|
||||||
-2 * s, -1 * s, 0,
|
|
||||||
-1 * s, 1, 1 * s,
|
|
||||||
0, 1 * s, 2 * s,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Blur Filter
|
|
||||||
_filter.blur = function (size) {
|
|
||||||
const blurSizeX = (size / 7) / _width;
|
|
||||||
const blurSizeY = (size / 7) / _height;
|
|
||||||
const program = _compileShader(_filter.blur.SHADER);
|
|
||||||
// Vertical
|
|
||||||
gl.uniform2f(program.uniform.px, 0, blurSizeY);
|
|
||||||
_draw(DRAW.INTERMEDIATE);
|
|
||||||
// Horizontal
|
|
||||||
gl.uniform2f(program.uniform.px, blurSizeX, 0);
|
|
||||||
_draw();
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.blur.SHADER = [
|
|
||||||
'precision highp float;',
|
|
||||||
'varying vec2 vUv;',
|
|
||||||
'uniform sampler2D texture;',
|
|
||||||
'uniform vec2 px;',
|
|
||||||
'void main(void) {',
|
|
||||||
'gl_FragColor = vec4(0.0);',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2(-7.0*px.x, -7.0*px.y))*0.0044299121055113265;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2(-6.0*px.x, -6.0*px.y))*0.00895781211794;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2(-5.0*px.x, -5.0*px.y))*0.0215963866053;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2(-4.0*px.x, -4.0*px.y))*0.0443683338718;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2(-3.0*px.x, -3.0*px.y))*0.0776744219933;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2(-2.0*px.x, -2.0*px.y))*0.115876621105;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2(-1.0*px.x, -1.0*px.y))*0.147308056121;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv )*0.159576912161;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2( 1.0*px.x, 1.0*px.y))*0.147308056121;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2( 2.0*px.x, 2.0*px.y))*0.115876621105;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2( 3.0*px.x, 3.0*px.y))*0.0776744219933;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2( 4.0*px.x, 4.0*px.y))*0.0443683338718;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2( 5.0*px.x, 5.0*px.y))*0.0215963866053;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2( 6.0*px.x, 6.0*px.y))*0.00895781211794;',
|
|
||||||
'gl_FragColor += texture2D(texture, vUv + vec2( 7.0*px.x, 7.0*px.y))*0.0044299121055113265;',
|
|
||||||
'}',
|
|
||||||
].join('\n');
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Pixelate Filter
|
|
||||||
_filter.pixelate = function (size) {
|
|
||||||
const blurSizeX = (size) / _width;
|
|
||||||
const blurSizeY = (size) / _height;
|
|
||||||
const program = _compileShader(_filter.pixelate.SHADER);
|
|
||||||
// Horizontal
|
|
||||||
gl.uniform2f(program.uniform.size, blurSizeX, blurSizeY);
|
|
||||||
_draw();
|
|
||||||
};
|
|
||||||
|
|
||||||
_filter.pixelate.SHADER = [
|
|
||||||
'precision highp float;',
|
|
||||||
'varying vec2 vUv;',
|
|
||||||
'uniform vec2 size;',
|
|
||||||
'uniform sampler2D texture;',
|
|
||||||
'vec2 pixelate(vec2 coord, vec2 size) {',
|
|
||||||
'return floor( coord / size ) * size;',
|
|
||||||
'}',
|
|
||||||
'void main(void) {',
|
|
||||||
'gl_FragColor = vec4(0.0);',
|
|
||||||
'vec2 coord = pixelate(vUv, size);',
|
|
||||||
'gl_FragColor += texture2D(texture, coord);',
|
|
||||||
'}',
|
|
||||||
].join('\n');
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
export const vertexIdentity = `
|
||||||
|
precision highp float;
|
||||||
|
attribute vec2 pos;
|
||||||
|
attribute vec2 uv;
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform float flipY;
|
||||||
|
void main(void) {
|
||||||
|
vUv = uv;
|
||||||
|
gl_Position = vec4(pos.x, pos.y*flipY, 0.0, 1.);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const fragmentIdentity = `
|
||||||
|
precision highp float;
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform sampler2D texture;
|
||||||
|
void main(void) {
|
||||||
|
gl_FragColor = texture2D(texture, vUv);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const colorMatrixWithAlpha = `
|
||||||
|
precision highp float;
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform sampler2D texture;
|
||||||
|
uniform float m[20];
|
||||||
|
void main(void) {
|
||||||
|
vec4 c = texture2D(texture, vUv);
|
||||||
|
gl_FragColor.r = m[0] * c.r + m[1] * c.g + m[2] * c.b + m[3] * c.a + m[4];
|
||||||
|
gl_FragColor.g = m[5] * c.r + m[6] * c.g + m[7] * c.b + m[8] * c.a + m[9];
|
||||||
|
gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[13] * c.a + m[14];
|
||||||
|
gl_FragColor.a = m[15] * c.r + m[16] * c.g + m[17] * c.b + m[18] * c.a + m[19];
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const colorMatrixWithoutAlpha = `
|
||||||
|
precision highp float;
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform sampler2D texture;
|
||||||
|
uniform float m[20];
|
||||||
|
void main(void) {
|
||||||
|
vec4 c = texture2D(texture, vUv);
|
||||||
|
gl_FragColor.r = m[0] * c.r + m[1] * c.g + m[2] * c.b + m[4];
|
||||||
|
gl_FragColor.g = m[5] * c.r + m[6] * c.g + m[7] * c.b + m[9];
|
||||||
|
gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[14];
|
||||||
|
gl_FragColor.a = c.a;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const pixelate = `
|
||||||
|
precision highp float;
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform vec2 size;
|
||||||
|
uniform sampler2D texture;
|
||||||
|
vec2 pixelate(vec2 coord, vec2 size) {
|
||||||
|
return floor( coord / size ) * size;
|
||||||
|
}
|
||||||
|
void main(void) {
|
||||||
|
gl_FragColor = vec4(0.0);
|
||||||
|
vec2 coord = pixelate(vUv, size);
|
||||||
|
gl_FragColor += texture2D(texture, coord);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const blur = `
|
||||||
|
precision highp float;
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform sampler2D texture;
|
||||||
|
uniform vec2 px;
|
||||||
|
void main(void) {
|
||||||
|
gl_FragColor = vec4(0.0);
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2(-7.0*px.x, -7.0*px.y))*0.0044299121055113265;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2(-6.0*px.x, -6.0*px.y))*0.00895781211794;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2(-5.0*px.x, -5.0*px.y))*0.0215963866053;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2(-4.0*px.x, -4.0*px.y))*0.0443683338718;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2(-3.0*px.x, -3.0*px.y))*0.0776744219933;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2(-2.0*px.x, -2.0*px.y))*0.115876621105;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2(-1.0*px.x, -1.0*px.y))*0.147308056121;
|
||||||
|
gl_FragColor += texture2D(texture, vUv )*0.159576912161;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2( 1.0*px.x, 1.0*px.y))*0.147308056121;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2( 2.0*px.x, 2.0*px.y))*0.115876621105;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2( 3.0*px.x, 3.0*px.y))*0.0776744219933;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2( 4.0*px.x, 4.0*px.y))*0.0443683338718;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2( 5.0*px.x, 5.0*px.y))*0.0215963866053;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2( 6.0*px.x, 6.0*px.y))*0.00895781211794;
|
||||||
|
gl_FragColor += texture2D(texture, vUv + vec2( 7.0*px.x, 7.0*px.y))*0.0044299121055113265;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const convolution = `
|
||||||
|
precision highp float;
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform sampler2D texture;
|
||||||
|
uniform vec2 px;
|
||||||
|
uniform float m[9];
|
||||||
|
void main(void) {
|
||||||
|
vec4 c11 = texture2D(texture, vUv - px); // top left
|
||||||
|
vec4 c12 = texture2D(texture, vec2(vUv.x, vUv.y - px.y)); // top center
|
||||||
|
vec4 c13 = texture2D(texture, vec2(vUv.x + px.x, vUv.y - px.y)); // top right
|
||||||
|
vec4 c21 = texture2D(texture, vec2(vUv.x - px.x, vUv.y) ); // mid left
|
||||||
|
vec4 c22 = texture2D(texture, vUv); // mid center
|
||||||
|
vec4 c23 = texture2D(texture, vec2(vUv.x + px.x, vUv.y) ); // mid right
|
||||||
|
vec4 c31 = texture2D(texture, vec2(vUv.x - px.x, vUv.y + px.y) ); // bottom left
|
||||||
|
vec4 c32 = texture2D(texture, vec2(vUv.x, vUv.y + px.y) ); // bottom center
|
||||||
|
vec4 c33 = texture2D(texture, vUv + px ); // bottom right
|
||||||
|
gl_FragColor =
|
||||||
|
c11 * m[0] + c12 * m[1] + c22 * m[2] +
|
||||||
|
c21 * m[3] + c22 * m[4] + c23 * m[5] +
|
||||||
|
c31 * m[6] + c32 * m[7] + c33 * m[8];
|
||||||
|
gl_FragColor.a = c22.a;
|
||||||
|
}
|
||||||
|
`;
|
Loading…
Reference in New Issue