Stencil buffer in WebGL

2020-04-20 17:24发布

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.

enter image description here

Please explain to me step by step use stencil buffer?

Link for my program

1条回答
三岁会撩人
2楼-- · 2020-04-20 17:39

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>

查看更多
登录 后发表回答