且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

WebGL中的模板缓冲区

更新时间:2023-01-24 21:58:03

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

To use the stencil buffer you have to first request it when you create the webgl context

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

然后打开模板测试

  gl.enable(gl.STENCIL_TEST);

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

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

现在我们可以绘制其他内容(较大的三角形),它只会绘制出模板缓冲区中除第一个三角形所在的位置之外的其他任何地方都为0的位置.

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.

示例:

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>