我正在使用WebGL 1.0。我在模板缓冲区上画了一个圆圈,现在我想多次使用此模板缓冲区而不清除它。第一次使用时,我通过以下方式启用模板测试:
gl.enable(GL.STENCIL_TEST);
然后,我将颜色绘制到颜色缓冲区中。此后,稍后我想再次绘制 ,但是这次我想剪辑到模板缓冲区中的 inverse 。我知道我可以再次使用模板缓冲区,但是由于我没有使用gl.stencilOp(GL.ZERO, GL.ZERO, GL.ZERO)
,因此模板缓冲区应该仍然存在,但其中应保留原始值。
我的问题是-是否有一种快速的WebGL方式来反转该模板缓冲区,还是我必须使用GL.INVERT
的模板操作再次执行绘图操作?
答案 0 :(得分:0)
假设您将模板清除为一个值并绘制到具有不同值的模板,则可以使用gl.stencilFunc(gl.EQUAL, value, 0xFFFF)
仅在模板与value
匹配的位置绘制。
示例:
模板下方的代码,带有一个1s的圆圈和一个2s的正方形。然后绘制3个场景。模板为0的场景只有立方体的场景,模板为1的场景只有球形的场景,模板为2的场景只有圆环飞过的场景
const m4 = twgl.m4;
const v3 = twgl.v3;
const gl = document.querySelector("canvas").getContext("webgl", {
stencil: true,
});
const programInfo = makeProgramInfo(gl);
const renderStencil1 = setupSceneStencil1();
const renderStencil2 = setupSceneStencil2();
const renderScene1 = setupScene1();
const renderScene2 = setupScene2();
const renderScene3 = setupScene3();
function render(time) {
time *= 0.001;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.disable(gl.STENCIL_TEST);
gl.clearStencil(0);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(1, 1, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
// draw 1s into stencil
gl.enable(gl.STENCIL_TEST);
gl.stencilFunc(gl.ALWAYS, 1, 0xFF);
if (time / 5 % 2 | 0) {
// this will end up with a 2s where the square overlaps the circle
gl.stencilOp(gl.INCR, gl.INCR, gl.INCR);
} else {
// this will end up with a 2s where the square is drawn
gl.stencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE);
}
gl.disable(gl.DEPTH_TEST);
renderStencil1(time);
// draw 2s into stencil
gl.stencilFunc(gl.ALWAYS, 2, 0xFF);
gl.disable(gl.DEPTH_TEST);
renderStencil2(time);
// draw where there are 0s
gl.enable(gl.DEPTH_TEST);
gl.stencilFunc(gl.EQUAL, 0, 0xFF);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
renderScene1(time);
// draw where there are 1s
gl.stencilFunc(gl.EQUAL, 1, 0xFF);
renderScene2(time);
// draw where there are 2s
gl.stencilFunc(gl.EQUAL, 2, 0xFF);
renderScene3(time);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
function setupSceneStencil1() {
const bufferInfo = twgl.primitives.createDiscBufferInfo(gl, 1, 48);
const color = [1, 0, 0, 1];
const tex = twgl.createTexture(gl, {
src: [255, 255, 255, 255],
});
function render(time, viewProjection) {
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
const s = 1 + (Math.sin(time) * .5 + .5) * 10;
let mat = m4.copy(viewProjection);
mat = m4.translate(mat, [
Math.sin(time * 1.7) * 3,
0,
0,
]);
mat = m4.scale(mat, [s, s, s]);
mat = m4.rotateX(mat, Math.PI * .5);
twgl.setUniforms(programInfo, {
u_diffuse: tex,
u_diffuseMult: color,
u_worldViewProjection: mat,
});
twgl.drawBufferInfo(gl, bufferInfo);
}
return setupScene(render);
}
function setupSceneStencil2() {
const bufferInfo = twgl.primitives.createPlaneBufferInfo(gl, 2, 2);
const color = [0, 0, 1, 1];
const tex = twgl.createTexture(gl, {
src: [255, 255, 255, 255],
});
function render(time, viewProjection) {
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
const s = 1 + (Math.cos(time * 2.3) * .5 + .5) * 5;
let mat = m4.copy(viewProjection);
mat = m4.translate(mat, [
Math.cos(time * 1.3) * 3,
0,
0,
]);
mat = m4.scale(mat, [s, s, s]);
mat = m4.rotateZ(mat, -time);
mat = m4.rotateX(mat, Math.PI * .5);
twgl.setUniforms(programInfo, {
u_diffuse: tex,
u_diffuseMult: color,
u_worldViewProjection: mat,
});
twgl.drawBufferInfo(gl, bufferInfo);
}
return setupScene(render);
}
function setupScene1() {
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 4);
const color = makeColor();
function render(time, viewProjection, tex) {
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
const numCubes = 20;
for (let i = 0; i < numCubes; ++i) {
const u = i / (numCubes - 1);
const uu = u * 2 - 1;
let mat = m4.copy(viewProjection);
mat = m4.translate(mat, [
uu * 15 + Math.sin(time),
((u * 8 + time) % 2 - 1) * 15,
0,
]);
mat = m4.rotateY(mat, u + time);
mat = m4.rotateX(mat, u + time);
twgl.setUniforms(programInfo, {
u_diffuse: tex,
u_diffuseMult: color,
u_worldViewProjection: mat,
});
twgl.drawBufferInfo(gl, bufferInfo);
}
}
return setupScene(render);
}
function setupScene2() {
const bufferInfo = twgl.primitives.createSphereBufferInfo(gl, 1, 24, 12);
const color = makeColor();
// adapted from http://stackoverflow.com/a/26127012/128511
// used to space the cubes around the sphere
function fibonacciSphere(samples, i) {
const rnd = 1.;
const offset = 2. / samples;
const increment = Math.PI * (3. - Math.sqrt(5.));
// for i in range(samples):
const y = ((i * offset) - 1.) + (offset / 2.);
const r = Math.sqrt(1. - Math.pow(y ,2.));
const phi = ((i + rnd) % samples) * increment;
const x = Math.cos(phi) * r;
const z = Math.sin(phi) * r;
return [x, y, z];
}
function render(time, viewProjection, tex) {
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
const numSpheres = 100;
for (let i = 0; i < numSpheres; ++i) {
const u = i / (numSpheres - 1);
const uu = u * 2 - 1;
let mat = m4.copy(viewProjection);
mat = m4.rotateY(mat, time);
mat = m4.rotateZ(mat, time);
mat = m4.translate(mat, v3.mulScalar(fibonacciSphere(numSpheres, i), 8));
mat = m4.rotateX(mat, u + time);
twgl.setUniforms(programInfo, {
u_diffuse: tex,
u_diffuseMult: color,
u_worldViewProjection: mat,
});
twgl.drawBufferInfo(gl, bufferInfo);
}
}
return setupScene(render);
}
function setupScene3() {
const bufferInfo = twgl.primitives.createTorusBufferInfo(gl, 2, 0.4, 24, 12);
const color = makeColor();
function render(time, viewProjection, tex) {
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
const numSpheres = 100;
for (let i = 0; i < numSpheres; ++i) {
const u = i / (numSpheres - 1);
const uu = u * 2 - 1;
let mat = m4.copy(viewProjection);
mat = m4.rotateZ(mat, time);
mat = m4.translate(mat, [0, 40, -20]);
mat = m4.rotateX(mat, time + u * Math.PI * 2);
mat = m4.translate(mat, [0, 40, 0]);
mat = m4.rotateX(mat, Math.PI * .5);
mat = m4.rotateY(mat, u * Math.PI * 20);
twgl.setUniforms(programInfo, {
u_diffuse: tex,
u_diffuseMult: color,
u_worldViewProjection: mat,
});
twgl.drawBufferInfo(gl, bufferInfo);
}
}
return setupScene(render);
}
function setupScene(renderFn) {
const camera = m4.identity();
const view = m4.identity();
const viewProjection = m4.identity();
const tex = twgl.createTexture(gl, {
min: gl.NEAREST,
mag: gl.NEAREST,
format: gl.LUMINANCE,
src: [
255, 192, 255, 192,
192, 255, 192, 255,
255, 192, 255, 192,
192, 255, 192, 255,
],
});
return function render(time) {
const projection = m4.perspective(
30 * Math.PI / 180,
gl.canvas.clientWidth / gl.canvas.clientHeight,
0.5,
100);
const eye = [0, 0, -20];
const target = [0, 0, 0];
const up = [0, 1, 0];
m4.lookAt(eye, target, up, camera);
m4.inverse(camera, view);
m4.multiply(projection, view, viewProjection);
renderFn(time, viewProjection, tex);
}
}
function rand(min, max) {
if (max === undefined) {
max = min;
min = 0;
}
return min + Math.random() * (max - min);
}
function makeProgramInfo(gl) {
const vs = `
uniform mat4 u_worldViewProjection;
attribute vec4 position;
attribute vec2 texcoord;
varying vec2 v_texcoord;
void main() {
v_texcoord = texcoord;
gl_Position = u_worldViewProjection * position;
}
`;
const fs = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_diffuse;
uniform vec4 u_diffuseMult;
void main() {
gl_FragColor = texture2D(u_diffuse, v_texcoord) * u_diffuseMult;
}
`;
return twgl.createProgramInfo(gl, [vs, fs]);
}
function makeColor() {
const color = [rand(1), rand(1), rand(1), 1];
color[rand(3) | 0] = .8;
return color;
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
当然还有LESS
,LEQUAL
,GREATER
,GEQUAL
,NOTEQUAL
,当然还有NEVER
和{{1 }}还有其他相反的可能性。