WebGL 中的模板缓冲区

Stencil buffer in WebGL

如何为最简单的程序使用模版缓冲区? 我已经阅读了许多关于它的不同主题,但我没有找到关于它的详细指南。 我想在创建的四面体的每一侧切出孔。

请逐步向我解释使用模板缓冲区?

Link for my program

要使用模板缓冲区,您必须在创建 webgl 上下文时首先请求它

const gl = someCanvasElement.getContext('webgl', {stencil: true});

然后你打开模板测试

  gl.enable(gl.STENCIL_TEST);

设置测试使其始终通过并将参考值设置为 1

  gl.stencilFunc(
     gl.ALWAYS,    // the test
     1,            // 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.REPLACE,  // what to do if both tests pass
  );

然后我们绘制第一个内三角形

... lots of setup for a single triangle ...

gl.drawArrays(...) or gl.drawElements(...)

然后我们更改测试,使其仅在模板为零时通过

  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
  );

现在我们可以绘制其他东西(较大的三角形),它只会在模板缓冲区中有 0 的地方绘制,除了第一个三角形被绘制的地方之外,它无处不在。

示例:

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>