Skip to content

Commit

Permalink
Merge pull request #52 from brianchirls/develop
Browse files Browse the repository at this point in the history
Release 2014-04-08
  • Loading branch information
brianchirls committed Apr 8, 2014
2 parents 393c132 + d94dfeb commit 422cc74
Show file tree
Hide file tree
Showing 64 changed files with 2,708 additions and 1,274 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2012 Brian Chirls
Copyright (c) 2012-2014 Brian Chirls

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Expand Down
26 changes: 10 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ Full documentation is in progress at the [wiki](https://github.com/brianchirls/S
- Load with [AMD](http://requirejs.org/docs/whyamd.html#amd)/[RequireJS](http://www.requirejs.org/)

### Included Effects
- Accumulator
- Ascii Text
- Bleach Bypass
- Blend
- Brightness/Contrast
- Channel Mapping
- Checkerboard Generator
- Chroma Key
- Color Complements
- Color Generator
Expand All @@ -37,24 +39,29 @@ Full documentation is in progress at the [wiki](https://github.com/brianchirls/S
- Edge Detect
- Emboss
- Exposure Adjust
- Expressions
- Fader
- False Color
- Film Grain
- Freeze Frame
- Gaussian Blur
- Hex Tiles
- Highlights/Shadows
- Hue/Saturation Adjust
- Invert
- Kaleidoscope
- Layers
- Linear Transfer
- Luma Key
- Night Vision
- Polar Coordinates
- Ripple
- Scanlines
- Sepia tone
- Simplex Noise
- Sketch
- Split
- Throttle Frame Rate
- Tone Adjust
- TV Glitch
- Vignette
Expand All @@ -65,9 +72,8 @@ Full documentation is in progress at the [wiki](https://github.com/brianchirls/S
#### WebGL

Seriously.js requires a browser that supports [WebGL](http://en.wikipedia.org/wiki/Webgl).
Development is targeted to and tested in Firefox (4.0+) and Google Chrome (9+). Safari
and Opera are [expected to support WebGL](http://caniuse.com/#search=webgl)
in the near future. Internet Explorer supports WebGL but does not support video textures.
Development is targeted to and tested in Firefox (4.0+), Google Chrome (9+), Internet Explorer (11+) and Opera (18+). Safari is [expected to support WebGL](http://caniuse.com/#search=webgl)
in the near future.

Even though a browser may support WebGL, the ability to run it depends
on the system's graphics card. Seriously.js is heavily optimized, so most
Expand All @@ -85,19 +91,7 @@ descriptive error messages wherever possible.
Due to security limitations of WebGL, Seriously.js can only process video
or images that are served from the same domain, unless they are served
with [CORS headers](http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/).
Firefox 12 and up [support CORS for video](https://bugzilla.mozilla.org/show_bug.cgi?id=682299) but other browsers do not, and videos served with CORS are rare.
So for now, it is best to host your own video files.

### Roadmap:

- API documentation and plugin developer guidelines
- More examples and tutorials
- 3D transforms (perspective) on any node
- Accept input from WebGL Textures
- Benchmarking utility to determine client capabilities
- Automatic resolution tuning to maintain minimum frame rate
- Handle lost WebGL context
- Graphical interface
Firefox, Chrome and Opera support CORS for video, but Safari and Internet Explorer do not, and videos served with CORS are rare. So for now, it is best to host your own video files.

## Contributing

Expand Down
255 changes: 255 additions & 0 deletions effects/seriously.accumulator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
/* global define, require */
(function (root, factory) {
'use strict';

if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['seriously'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS
factory(require('seriously'));
} else {
if (!root.Seriously) {
root.Seriously = { plugin: function (name, opt) { this[name] = opt; } };
}
factory(root.Seriously);
}
}(this, function (Seriously, undefined) {
'use strict';

/*
Adapted from blend mode shader by Romain Dura
http://mouaif.wordpress.com/2009/01/05/photoshop-math-with-glsl-shaders/
*/

function vectorBlendFormula(formula, base, blend) {
function replace(channel) {
var r = {
base: (base || 'base') + '.' + channel,
blend: (blend || 'blend') + '.' + channel
};
return function (match) {
return r[match] || match;
};
}

return 'vec3(' +
formula.replace(/blend|base/g, replace('r')) + ', ' +
formula.replace(/blend|base/g, replace('g')) + ', ' +
formula.replace(/blend|base/g, replace('b')) +
')';
}

var blendModes = {
normal: 'blend',
lighten: 'max(blend, base)',
darken: 'min(blend, base)',
multiply: '(base * blend)',
average: '(base + blend / TWO)',
add: 'min(base + blend, ONE)',
subtract: 'max(base + blend - ONE, ZERO)',
difference: 'abs(base - blend)',
negation: '(ONE - abs(ONE - base - blend))',
exclusion: '(base + blend - TWO * base * blend)',
screen: '(ONE - ((ONE - base) * (ONE - blend)))',
lineardodge: 'min(base + blend, ONE)',
phoenix: '(min(base, blend) - max(base, blend) + ONE)',
linearburn: 'max(base + blend - ONE, ZERO)', //same as subtract?

overlay: vectorBlendFormula('base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))'),
softlight: vectorBlendFormula('blend < 0.5 ? (2.0 * base * blend + base * base * (1.0 - 2.0 * blend)) : (sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend))'),
hardlight: vectorBlendFormula('base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))', 'blend', 'base'),
colordodge: vectorBlendFormula('blend == 1.0 ? blend : min(base / (1.0 - blend), 1.0)'),
colorburn: vectorBlendFormula('blend == 0.0 ? blend : max((1.0 - ((1.0 - base) / blend)), 0.0)'),
linearlight: vectorBlendFormula('BlendLinearLightf(base, blend)'),
vividlight: vectorBlendFormula('BlendVividLightf(base, blend)'),
pinlight: vectorBlendFormula('BlendPinLightf(base, blend)'),
hardmix: vectorBlendFormula('BlendHardMixf(base, blend)'),
reflect: vectorBlendFormula('BlendReflectf(base, blend)'),
glow: vectorBlendFormula('BlendReflectf(blend, base)')
},

/*
All blend modes other than "normal" effectively act as adjustment layers,
so the alpha channel of the resulting image is just a copy of the "bottom"
or "destination" layer. The "top" or "source" alpha is only used to dampen
the color effect.
*/
mixAlpha = {
normal: true
};

Seriously.plugin('accumulator', function () {
var drawOpts = {
clear: false
},
frameBuffers,
fbIndex = 0;

return {
initialize: function (initialize, gl) {
initialize();
frameBuffers = [
this.frameBuffer,
new Seriously.util.FrameBuffer(gl, this.width, this.height)
];
},
shader: function (inputs, shaderSource) {
var mode = inputs.blendMode || 'normal';
mode = mode.toLowerCase();

shaderSource.fragment = [
'precision mediump float;',

'const vec3 ZERO = vec3(0.0);',
'const vec3 ONE = vec3(1.0);',
'const vec3 HALF = vec3(0.5);',
'const vec3 TWO = vec3(2.0);',

'#define BlendAddf(base, blend) min(base + blend, 1.0)',
'#define BlendSubtractf(base, blend) max(base + blend - 1.0, 0.0)',
'#define BlendLinearDodgef(base, blend) BlendAddf(base, blend)',
'#define BlendLinearBurnf(base, blend) BlendSubtractf(base, blend)',
'#define BlendLightenf(base, blend) max(blend, base)',
'#define BlendDarkenf(base, blend) min(blend, base)',
'#define BlendLinearLightf(base, blend) (blend < 0.5 ? BlendLinearBurnf(base, (2.0 * blend)) : BlendLinearDodgef(base, (2.0 * (blend - 0.5))))',
'#define BlendScreenf(base, blend) (1.0 - ((1.0 - base) * (1.0 - blend)))',
'#define BlendOverlayf(base, blend) (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)))',
'#define BlendSoftLightf(base, blend) ((blend < 0.5) ? (2.0 * base * blend + base * base * (1.0 - 2.0 * blend)) : (sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend)))',
'#define BlendColorDodgef(base, blend) ((blend == 1.0) ? blend : min(base / (1.0 - blend), 1.0))',
'#define BlendColorBurnf(base, blend) ((blend == 0.0) ? blend : max((1.0 - ((1.0 - base) / blend)), 0.0))',
'#define BlendVividLightf(base, blend) ((blend < 0.5) ? BlendColorBurnf(base, (2.0 * blend)) : BlendColorDodgef(base, (2.0 * (blend - 0.5))))',
'#define BlendPinLightf(base, blend) ((blend < 0.5) ? BlendDarkenf(base, (2.0 * blend)) : BlendLightenf(base, (2.0 *(blend - 0.5))))',
'#define BlendHardMixf(base, blend) ((BlendVividLightf(base, blend) < 0.5) ? 0.0 : 1.0)',
'#define BlendReflectf(base, blend) ((blend == 1.0) ? blend : min(base * base / (1.0 - blend), 1.0))',

/*
Linear Light is another contrast-increasing mode
If the blend color is darker than midgray, Linear Light darkens the image
by decreasing the brightness. If the blend color is lighter than midgray,
the result is a brighter image due to increased brightness.
*/

'#define BlendFunction(base, blend) ' + blendModes[mode],
(mixAlpha[mode] ? '#define MIX_ALPHA' : ''),

'varying vec2 vTexCoord;',

'uniform sampler2D source;',
'uniform sampler2D previous;',

'uniform float opacity;',

'vec3 BlendOpacity(vec4 base, vec4 blend, float opacity) {',
//apply blend, then mix by (opacity * blend.a)
' vec3 blendedColor = BlendFunction(base.rgb, blend.rgb);',
' return mix(base.rgb, blendedColor, opacity * blend.a);',
'}',

'void main(void) {',
' vec4 topPixel = texture2D(source, vTexCoord);',
' vec4 bottomPixel = texture2D(previous, vTexCoord);',

' if (topPixel.a == 0.0) {',
' gl_FragColor = bottomPixel;',
' } else {',
' float alpha;',
'#ifdef MIX_ALPHA',
' alpha = topPixel.a * opacity;',
' alpha = alpha + bottomPixel.a * (1.0 - alpha);',
'#else',
' alpha = bottomPixel.a;',
'#endif',
' gl_FragColor = vec4(BlendOpacity(bottomPixel, topPixel, opacity), alpha);',
' }',
'}'
].join('\n');

return shaderSource;
},
resize: function () {
if (frameBuffers) {
frameBuffers[0].resize(this.width, this.height);
frameBuffers[1].resize(this.width, this.height);
}
},
draw: function (shader, model, uniforms, frameBuffer, draw) {
var fb;

// ping-pong textures
this.uniforms.previous = this.frameBuffer.texture;
fbIndex = (fbIndex + 1) % 2;
fb = frameBuffers[fbIndex];
this.frameBuffer = fb;
this.texture = fb.texture;

if (this.inputs.clear) {
draw(this.baseShader, model, uniforms, fb.frameBuffer, null);
return;
}

draw(shader, model, uniforms, fb.frameBuffer, null, drawOpts);
},
destroy: function () {
if (frameBuffers) {
frameBuffers[0].destroy();
frameBuffers[1].destroy();
frameBuffers.length = 0;
}
}
};
}, {
inPlace: false,
title: 'Accumulator',
description: 'Draw on top of previous frame',
inputs: {
source: {
type: 'image',
uniform: 'source'
},
clear: {
type: 'boolean',
defaultValue: false
},
opacity: {
type: 'number',
uniform: 'opacity',
defaultValue: 1,
min: 0,
max: 1
},
blendMode: {
type: 'enum',
shaderDirty: true,
defaultValue: 'normal',
options: [
['normal', 'Normal'],
['lighten', 'Lighten'],
['darken', 'Darken'],
['multiply', 'Multiply'],
['average', 'Average'],
['add', 'Add'],
['substract', 'Substract'],
['difference', 'Difference'],
['negation', 'Negation'],
['exclusion', 'Exclusion'],
['screen', 'Screen'],
['overlay', 'Overlay'],
['softlight', 'Soft Light'],
['hardlight', 'Hard Light'],
['colordodge', 'Color Dodge'],
['colorburn', 'Color Burn'],
['lineardodge', 'Linear Dodge'],
['linearburn', 'Linear Burn'],
['linearlight', 'Linear Light'],
['vividlight', 'Vivid Light'],
['pinlight', 'Pin Light'],
['hardmix', 'Hard Mix'],
['reflect', 'Reflect'],
['glow', 'Glow'],
['phoenix', 'Phoenix']
]
}
}
});
}));
Loading

0 comments on commit 422cc74

Please sign in to comment.