mirror of https://github.com/vladmandic/human
update imagefx
parent
93b5dfd3ee
commit
c7dbca2856
|
@ -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 {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||||
|
}
|
||||||
|
`;
|
2304
test/build.log
2304
test/build.log
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue