Одним из способов будет использование теста трафарета. Вы задали бы webgl так, чтобы при рисовании пикселя трафарет сохранял определенное значение, а тест трафарета устанавливался таким образом, чтобы он не проходил, если он видит это значение.
Сначала приведем пример, в котором 2 набораперекрывающиеся треугольники с наложением. Пары темнеют там, где они перекрываются
function main() {
const m4 = twgl.m4;
const gl = document
.querySelector('canvas')
.getContext('webgl');
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;
}
`;
// compile shader, link program, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
gl.useProgram(programInfo.program);
// create a buffer and put data in it
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: {
numComponents: 2,
data: [
-0.5, -0.2,
0.5, -0.2,
0.5, 0.2,
-0.2, -0.5,
-0.2, 0.5,
0.2, 0.5,
],
},
});
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// calls gl.uniform??
twgl.setUniforms(programInfo, {
color: [0.5, 0, 0, 0.5],
matrix: m4.identity(),
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
twgl.setUniforms(programInfo, {
color: [0, 0, 0.5, 0.5],
matrix: m4.rotateZ(
m4.translation([-0.1, 0.2, 0]),
Math.PI * 1.2),
});
twgl.drawBufferInfo(gl, bufferInfo);
}
main();
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
Затем тот же пример с тестом трафарета на
Сначала нам нужно запросить буфер трафарета
const gl = someCanvas.getContext('webgl2', {stencil: true});
Затем мы включаем тест трафарета
gl.enable(gl.STENCIL_TEST);
Настройте тест так, чтобы он рисовал, только если буфер трафарета равен нулю
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.INCR, // what to do if both tests pass
);
Между первым тиражом и вторым очищаем буфер трафарета
gl.clear(gl.STENCIL_BUFFER_BIT);
Пример
function main() {
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;
}
`;
// compile shader, link program, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
gl.useProgram(programInfo.program);
// create a buffer and put data in it
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: {
numComponents: 2,
data: [
-0.5, -0.2,
0.5, -0.2,
0.5, 0.2,
-0.2, -0.5,
-0.2, 0.5,
0.2, 0.5,
],
},
});
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.STENCIL_TEST);
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.INCR, // what to do if both tests pass
);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// calls gl.uniform??
twgl.setUniforms(programInfo, {
color: [0.5, 0, 0, 0.5],
matrix: m4.identity(),
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
gl.clear(gl.STENCIL_BUFFER_BIT);
twgl.setUniforms(programInfo, {
color: [0, 0, 0.5, 0.5],
matrix: m4.rotateZ(
m4.translation([-0.1, 0.2, 0]),
Math.PI * 1.2),
});
twgl.drawBufferInfo(gl, bufferInfo);
}
main();
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
Другое решение, вы также можете использовать тест глубины, если вы рисуете 2D-вещи. Тест глубины по умолчанию отрисовывается, только если глубина gl.LESS
текущей глубины, поэтому включение теста глубины и установка другой глубины между отрисовками также будет работать, если глубина треугольников одинакова. Вы можете вычислить различное значение глубины для каждой нарисованной вещи, вам нужно найти разрешение в битах буфера глубины. Или вы можете использовать gl.polygonOffset
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.POLYGON_OFFSET_FILL);
... then ...
for (let i = 0; i < numThingsToDraw; ++i) {
gl.polygonOffset(0, -i); // each thing 1 depth unit less
draw2DThing(things[i]);
}
пример
function main() {
const m4 = twgl.m4;
const gl = document
.querySelector('canvas')
.getContext('webgl');
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;
}
`;
// compile shader, link program, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
gl.useProgram(programInfo.program);
// create a buffer and put data in it
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: {
numComponents: 2,
data: [
-0.5, -0.2,
0.5, -0.2,
0.5, 0.2,
-0.2, -0.5,
-0.2, 0.5,
0.2, 0.5,
],
},
});
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.POLYGON_OFFSET_FILL);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// calls gl.uniform??
twgl.setUniforms(programInfo, {
color: [0.5, 0, 0, 0.5],
matrix: m4.identity(),
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
gl.polygonOffset(0, -1);
twgl.setUniforms(programInfo, {
color: [0, 0, 0.5, 0.5],
matrix: m4.rotateZ(
m4.translation([-0.1, 0.2, 0.0]),
Math.PI * 1.2),
});
twgl.drawBufferInfo(gl, bufferInfo);
}
main();
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>