How I can use to stencil buffer for my easiest program? I have read many different themes about it, but I not found a detailed guide about it. I want to cut out for hole each side in a created tetrahedron.
Please explain to me step by step use stencil buffer?
To use the stencil buffer you have to first request it when you create the webgl context
const gl = someCanvasElement.getContext('webgl', {stencil: true});
Then you turn on the stencil test
gl.enable(gl.STENCIL_TEST);
Set up the test so it always passes and set the reference value to 1
gl.stencilFunc(
gl.ALWAYS, // the test
1, // reference value
0xFF, // mask
);
And set the operation so we'll set the stencil to the reference value when both the stencil and depth tests pass
gl.stencilOp(
gl.KEEP, // what to do if the stencil test fails
gl.KEEP, // what to do if the depth test fails
gl.REPLACE, // what to do if both tests pass
);
We then draw the first inner triangle
... lots of setup for a single triangle ...
gl.drawArrays(...) or gl.drawElements(...)
Then we change the test so it only passes if the stencil is zero
gl.stencilFunc(
gl.EQUAL, // the test
0, // reference value
0xFF, // mask
);
gl.stencilOp(
gl.KEEP, // what to do if the stencil test fails
gl.KEEP, // what to do if the depth test fails
gl.KEEP, // what to do if both tests pass
);
and now we can draw something else (the larger triangle) and it will only draw where there is 0 in the stencil buffer which is everywhere except where the first triangle was drawn.
Example:
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl', {stencil: true});
const vs = `
attribute vec4 position;
uniform mat4 matrix;
void main() {
gl_Position = matrix * position;
}
`;
const fs = `
precision mediump float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}
`;
const program = twgl.createProgram(gl, [vs, fs]);
const posLoc = gl.getAttribLocation(program, 'position');
const matLoc = gl.getUniformLocation(program, 'matrix');
const colorLoc = gl.getUniformLocation(program, 'color');
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, -1,
1, 1,
-1, 1,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(
posLoc, // attribute location
2, // 2 value per vertex
gl.FLOAT, // 32bit floating point values
false, // don't normalize
0, // stride (0 = base on type and size)
0, // offset into buffer
);
// clear the stencil to 0 (the default)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
gl.useProgram(program);
// turn on the stencil
gl.enable(gl.STENCIL_TEST);
// Set the stencil test so it always passes
// and the reference to 1
gl.stencilFunc(
gl.ALWAYS, // the test
1, // reference value
0xFF, // mask
);
// Set it so we replace with the reference value (1)
gl.stencilOp(
gl.KEEP, // what to do if the stencil test fails
gl.KEEP, // what to do if the depth test fails
gl.REPLACE, // what to do if both tests pass
);
// draw a white small triangle
gl.uniform4fv(colorLoc, [1, 1, 1, 1]); // white
gl.uniformMatrix4fv(matLoc, false, m4.scaling([0.2, 0.2, 1]));
gl.drawArrays(gl.TRIANGLES, 0, 3);
// Set the test that the stencil must = 0
gl.stencilFunc(
gl.EQUAL, // the test
0, // reference value
0xFF, // mask
);
// don't change the stencil buffer on draw
gl.stencilOp(
gl.KEEP, // what to do if the stencil test fails
gl.KEEP, // what to do if the depth test fails
gl.KEEP, // what to do if both tests pass
);
// draw a large green triangle
gl.uniform4fv(colorLoc, [0, 1, 0, 1]); // green
gl.uniformMatrix4fv(matLoc, false, m4.scaling([0.9, -0.9, 1]));
gl.drawArrays(gl.TRIANGLES, 0, 3);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>