Reputation: 1093
I've been investigating a way to create a clipping mask similar to this one (done in SVG).
Based on my findings I chose to achieve this through stenciling. My implementation however is grossly incorrect. I'm not entirely sure how gl.stencilOp
and gl.stencilFunc
work because it seems like I need to render the fragment masking my main content twice. Once before I render the main content and once after with varying parameters.
Here's the working test: https://dl.dropboxusercontent.com/u/1595444/experiments/two.js/issues/issue-56/stencil-buffer/test/clip.html
The relevant snippet / portion of this test can be found in ../src/renderers/webgl.js
starting at L67
:
if (this._mask) {
gl.enable(gl.STENCIL_TEST);
gl.stencilFunc(gl.ALWAYS, 1, 1);
gl.colorMask(false, false, false, true);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);
// Renders the mask through gl.drawArrays L111
webgl[this._mask._renderer.type].render.call(
this._mask, gl, program, this);
gl.colorMask(true, true, true, true);
gl.stencilFunc(gl.NOTEQUAL, 0, 1);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
}
// Renders main content through a series of gl.drawArrays calls
_.each(this.children, webgl.group.renderChild, {
gl: gl,
program: program
});
if (this._mask) {
gl.colorMask(false, false, false, false);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR);
// Re-render mask so main content doesn't flicker
webgl[this._mask._renderer.type].render.call(
this._mask, gl, program, this);
gl.colorMask(true, true, true, true);
gl.stencilFunc(gl.NOTEQUAL, 0, 1);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
gl.disable(gl.STENCIL_TEST);
}
Guidance to emulate the webgl stenciling to work like the svg example would be much appreciated.
Upvotes: 2
Views: 2817
Reputation: 1646
What you need to do is:
As follow:
if (this._mask) {
// Clearing the stencil buffer
gl.clearStencil(0);
gl.clear(gl.STENCIL_BUFFER_BIT);
// Replacing the values at the stencil buffer to 1 on every pixel we draw
gl.stencilFunc(gl.ALWAYS, 1, 1);
gl.stencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE);
// disable color (u can also disable here the depth buffers)
gl.colorMask(false, false, false, false);
gl.enable(gl.STENCIL_TEST);
// Renders the mask through gl.drawArrays L111
webgl[this._mask._renderer.type].render.call(this._mask, gl, program, this);
// Telling the stencil now to draw/keep only pixels that equals 1 - which we set earlier
gl.stencilFunc(gl.EQUAL, 1, 1);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
// enabling back the color buffer
gl.colorMask(true, true, true, true);
}
// Renders main content through a series of gl.drawArrays calls
_.each(this.children, webgl.group.renderChild, {
gl: gl,
program: program
});
// Stop considering the stencil
if (this._mask) {
gl.disable(gl.STENCIL_TEST);
}
Upvotes: 3