不要在 webgl 中混合与自身交叉的多段线
Don't blend a polyline that has crossed lines with itself in webgl
我正在启用混合的 webgl 中绘制两条多段线(它们是示例中的线)。
gl.uniform4f(colorUniformLocation, 0, 0, 0, 0.3);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0.2, -1, 0.2, 1,]), gl.STATIC_DRAW);
gl.drawArrays(gl.LINE_STRIP, 0, 2);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, -1, 0, 1,0, -1, 0, 1]), gl.STATIC_DRAW);
gl.drawArrays(gl.LINE_STRIP, 0, 4);
Here 是codepen样本。
左边那条线是自己交叉的,好像和自己融为一体,所以颜色变深了。
我希望在这些多段线之间进行混合,但不希望多段线与自身混合。有办法吗?
一种方法是使用模板测试。您将设置 webgl,以便在绘制像素时模板存储特定值,并且您将设置模板测试,以便它在看到该值时失败。
首先是绘制 2 组 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>
我正在启用混合的 webgl 中绘制两条多段线(它们是示例中的线)。
gl.uniform4f(colorUniformLocation, 0, 0, 0, 0.3);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0.2, -1, 0.2, 1,]), gl.STATIC_DRAW);
gl.drawArrays(gl.LINE_STRIP, 0, 2);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, -1, 0, 1,0, -1, 0, 1]), gl.STATIC_DRAW);
gl.drawArrays(gl.LINE_STRIP, 0, 4);
Here 是codepen样本。
左边那条线是自己交叉的,好像和自己融为一体,所以颜色变深了。
我希望在这些多段线之间进行混合,但不希望多段线与自身混合。有办法吗?
一种方法是使用模板测试。您将设置 webgl,以便在绘制像素时模板存储特定值,并且您将设置模板测试,以便它在看到该值时失败。
首先是绘制 2 组 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>