playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
126 lines (104 loc) • 3.91 kB
JavaScript
// --------------- POST EFFECT DEFINITION --------------- //
/**
* @class
* @name EdgeDetectEffect
* @classdesc Edge Detection post effect using Sobel filter.
* @description Creates new instance of the post effect.
* @augments PostEffect
* @param {GraphicsDevice} graphicsDevice - The graphics device of the application.
*/
class EdgeDetectEffect extends pc.PostEffect {
constructor(graphicsDevice) {
super(graphicsDevice);
const fshader = /* glsl */`
uniform sampler2D uColorBuffer;
varying vec2 vUv0;
uniform vec2 uResolution;
uniform float uIntensity;
uniform vec4 uColor;
mat3 G[2];
const mat3 g0 = mat3( 1.0, 2.0, 1.0, 0.0, 0.0, 0.0, -1.0, -2.0, -1.0 );
const mat3 g1 = mat3( 1.0, 0.0, -1.0, 2.0, 0.0, -2.0, 1.0, 0.0, -1.0 );
void main(void)
{
mat3 I;
float cnv[2];
vec3 sample;
G[0] = g0;
G[1] = g1;
// Fetch the 3x3 neighbourhood and use the RGB vector's length as intensity value
for (float i = 0.0; i < 3.0; i++)
{
for (float j = 0.0; j < 3.0; j++)
{
sample = texture2D(uColorBuffer, vUv0 + uResolution * vec2(i - 1.0, j - 1.0)).rgb;
I[int(i)][int(j)] = length(sample);
}
}
// Calculate the convolution values for all the masks
for (int i=0; i<2; i++)
{
float dp3 = dot(G[i][0], I[0]) + dot(G[i][1], I[1]) + dot(G[i][2], I[2]);
cnv[i] = dp3 * dp3;
}
gl_FragColor = uIntensity * uColor * vec4(sqrt(cnv[0]*cnv[0]+cnv[1]*cnv[1]));
}
`;
this.shader = pc.ShaderUtils.createShader(graphicsDevice, {
uniqueName: 'EdgeDetectShader',
attributes: { aPosition: pc.SEMANTIC_POSITION },
vertexGLSL: pc.PostEffect.quadVertexShader,
fragmentGLSL: fshader
});
// Uniforms
this.resolution = new Float32Array(2);
this.intensity = 1.0;
this.color = new pc.Color(1, 1, 1, 1);
}
render(inputTarget, outputTarget, rect) {
const device = this.device;
const scope = device.scope;
this.resolution[0] = 1 / inputTarget.width;
this.resolution[1] = 1 / inputTarget.height;
scope.resolve('uResolution').setValue(this.resolution);
scope.resolve('uColorBuffer').setValue(inputTarget.colorBuffer);
scope.resolve('uColor').setValue(this.color.data);
scope.resolve('uIntensity').setValue(this.intensity);
this.drawQuad(outputTarget, this.shader, rect);
}
}
// ----------------- SCRIPT DEFINITION ------------------ //
var EdgeDetect = pc.createScript('edgeDetect');
EdgeDetect.attributes.add('intensity', {
type: 'number',
default: 1,
min: 0,
max: 2,
title: 'Intensity'
});
EdgeDetect.attributes.add('color', {
type: 'rgba',
default: [0.5, 0.5, 0.5, 1],
title: 'Color'
});
// initialize code called once per entity
EdgeDetect.prototype.initialize = function () {
this.effect = new EdgeDetectEffect(this.app.graphicsDevice);
this.effect.intensity = this.intensity;
this.effect.color = this.color;
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);
});
};