playcanvas
Version:
PlayCanvas WebGL game engine
118 lines (102 loc) • 3.92 kB
JavaScript
// --------------- POST EFFECT DEFINITION --------------- //
/**
* @class
* @name HueSaturationEffect
* @classdesc Allows hue and saturation adjustment of the input render target.
* @description Creates new instance of the post effect.
* @augments PostEffect
* @param {GraphicsDevice} graphicsDevice - The graphics device of the application.
* @property {number} hue Controls the hue. Ranges from -1 to 1 (-1 is 180 degrees in the negative direction, 0 no change, 1 is 180 degrees in the postitive direction).
* @property {number} saturation Controls the saturation. Ranges from -1 to 1 (-1 is solid gray, 0 no change, 1 maximum saturation).
*/
function HueSaturationEffect(graphicsDevice) {
pc.PostEffect.call(this, graphicsDevice);
// Shader author: tapio / http://tapio.github.com/
var fshader = [
'uniform sampler2D uColorBuffer;',
'uniform float uHue;',
'uniform float uSaturation;',
'',
'varying vec2 vUv0;',
'',
'void main() {',
' gl_FragColor = texture2D( uColorBuffer, vUv0 );',
'',
// uHue
' float angle = uHue * 3.14159265;',
' float s = sin(angle), c = cos(angle);',
' vec3 weights = (vec3(2.0 * c, -sqrt(3.0) * s - c, sqrt(3.0) * s - c) + 1.0) / 3.0;',
' float len = length(gl_FragColor.rgb);',
' gl_FragColor.rgb = vec3(',
' dot(gl_FragColor.rgb, weights.xyz),',
' dot(gl_FragColor.rgb, weights.zxy),',
' dot(gl_FragColor.rgb, weights.yzx)',
' );',
'',
// uSaturation
' float average = (gl_FragColor.r + gl_FragColor.g + gl_FragColor.b) / 3.0;',
' if (uSaturation > 0.0) {',
' gl_FragColor.rgb += (average - gl_FragColor.rgb) * (1.0 - 1.0 / (1.001 - uSaturation));',
' } else {',
' gl_FragColor.rgb += (average - gl_FragColor.rgb) * (-uSaturation);',
' }',
'}'
].join('\n');
this.shader = pc.ShaderUtils.createShader(graphicsDevice, {
uniqueName: 'HueSaturationShader',
attributes: { aPosition: pc.SEMANTIC_POSITION },
vertexGLSL: pc.PostEffect.quadVertexShader,
fragmentGLSL: fshader
});
// uniforms
this.hue = 0;
this.saturation = 0;
}
HueSaturationEffect.prototype = Object.create(pc.PostEffect.prototype);
HueSaturationEffect.prototype.constructor = HueSaturationEffect;
Object.assign(HueSaturationEffect.prototype, {
render: function (inputTarget, outputTarget, rect) {
var device = this.device;
var scope = device.scope;
scope.resolve('uHue').setValue(this.hue);
scope.resolve('uSaturation').setValue(this.saturation);
scope.resolve('uColorBuffer').setValue(inputTarget.colorBuffer);
this.drawQuad(outputTarget, this.shader, rect);
}
});
// ----------------- SCRIPT DEFINITION ------------------ //
var HueSaturation = pc.createScript('hueSaturation');
HueSaturation.attributes.add('hue', {
type: 'number',
default: 0,
min: -1,
max: 1,
title: 'Hue'
});
HueSaturation.attributes.add('saturation', {
type: 'number',
default: 0,
min: -1,
max: 1,
title: 'Saturation'
});
HueSaturation.prototype.initialize = function () {
this.effect = new HueSaturationEffect(this.app.graphicsDevice);
this.effect.hue = this.hue;
this.effect.saturation = this.saturation;
this.on('attr', function (name, value) {
this.effect[name] = value;
}, this);
var queue = this.entity.camera.postEffects;
queue.addEffect(this.effect);
this.on('state', function (enabled) {
if (enabled) {
queue.addEffect(this.effect);
} else {
queue.removeEffect(this.effect);
}
});
this.on('destroy', function () {
queue.removeEffect(this.effect);
});
};