根据原始顶点在 WEBGL 片段着色器中裁剪
Clipping in WEBGL fragment shader according primitive vertices
我是 WEBGL 的新手,我阅读了很多关于片段着色器的帖子。但是,我不知道如何访问片段着色器中的原始顶点(或每个顶点的标志)。
我的目标是在片段的至少一个原始顶点被剪裁后丢弃该片段。
感谢帮助
您不能直接访问片段着色器中的顶点。默认情况下,片段着色器获取的唯一数据是屏幕坐标(canvas/framebuffer 坐标)和当前光栅化像素的深度缓冲区值。您必须传递的所有其他数据。
最重要的是,在顶点着色器中,您可以计算顶点是否被剪裁并将该信息作为变量传递给片段着色器。如果没有被裁剪,你将传递 0,如果被裁剪,你将传递 1。 varyings 得到插值,因此如果在片段着色器中 varying 大于 0,则其中一个顶点被剪裁。
const vs = `
attribute vec4 position;
uniform mat4 matrix;
varying float clipped;
void main() {
gl_Position = matrix * position;
clipped = (
any(lessThan(gl_Position.xyz, -gl_Position.www)) ||
any(greaterThan(gl_Position.xyz, gl_Position.www))
) ? 1.0
: 0.0;
}
`;
const fs = `
precision highp float;
varying float clipped;
void main() {
if (clipped > 0.0) {
discard;
}
gl_FragColor = gl_FrontFacing ? vec4(1, 0, 0, 1) : vec4(0, 0, 1, 1);
}
`;
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
// compile shaders, link program, look up locations
const prgInfo = twgl.createProgramInfo(gl, [vs, fs]);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each of
// position, normals, texcoord, indices of a sphere.
const bufferInfo = twgl.primitives.createSphereBufferInfo(gl, 1, 8, 8);
function render(time) {
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.enable(gl.DEPTH_TEST);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer for each attribute
twgl.setBuffersAndAttributes(gl, prgInfo, bufferInfo);
gl.useProgram(prgInfo.program);
const fov = 60 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const near = 0.1;
const far = 5.0;
const mat = m4.perspective(fov, aspect, near, far);
m4.translate(mat, [
Math.sin(time / 1200),
Math.sin(time / 1300),
Math.sin(time / 1400) - 1.8,
], mat);
m4.rotateX(mat, time / 1000, mat);
m4.rotateY(mat, time / 1100, mat);
// calls gl.uniform
twgl.setUniforms(prgInfo, {
matrix: mat,
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
可能有更有效的方法,比如将三角形的所有顶点传递给顶点着色器,如果其中任何一个被剪裁,则设置所有顶点被剪裁,这样三角形就不会被栅格化。
const vs = `
attribute vec4 position;
attribute vec4 position1; // second vertex for triangle
attribute vec4 position2; // third vertex for triangle
uniform mat4 matrix;
bool vertexClipped(vec4 clipspace) {
return any(lessThan(clipspace.xyz, -clipspace.www)) ||
any(greaterThan(clipspace.xyz, clipspace.www));
}
void main() {
gl_Position = matrix * position;
vec4 clipPosition1 = matrix * position1;
vec4 clipPosition2 = matrix * position2;
bool clipped = vertexClipped(gl_Position) ||
vertexClipped(clipPosition1) ||
vertexClipped(clipPosition2);
if (clipped) {
gl_Position = vec4(vec3(2), 1); // some offscreen value
}
}
`;
const fs = `
precision highp float;
void main() {
gl_FragColor = gl_FrontFacing ? vec4(1, 0, 0, 1) : vec4(0, 0, 1, 1);
}
`;
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
// compile shaders, link program, look up locations
const prgInfo = twgl.createProgramInfo(gl, [vs, fs]);
const verts = twgl.primitives.deindexVertices(twgl.primitives.createSphereVertices(1, 8, 8));
// copy the positions
const position1 = new Float32Array(verts.position);
const position2 = new Float32Array(verts.position);
// shift the positions so we can pass all 3 vertices for each triangle
// to the vertex shader for each iteration
for (let i = 0; i < position1.length; i += 9) {
{
// 0, 1, 2 => 1, 2, 0
const temp = position1.slice(i, i + 3);
position1.set(position1.slice(i + 3, i + 9), i);
position1.set(temp, i + 6);
}
{
// 0, 1, 2 => 2, 0, 1
const temp = position2.slice(i + 6, i + 9);
position2.set(position2.slice(i + 0, i + 6), i + 3);
position2.set(temp, i);
}
}
verts.position1 = position1;
verts.position2 = position2;
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each of
// position, normals, texcoord, indices of a sphere.
const bufferInfo = twgl.createBufferInfoFromArrays(gl, verts);
function render(time) {
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.enable(gl.DEPTH_TEST);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer for each attribute
twgl.setBuffersAndAttributes(gl, prgInfo, bufferInfo);
gl.useProgram(prgInfo.program);
const fov = 60 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const near = 0.1;
const far = 5.0;
const mat = m4.perspective(fov, aspect, near, far);
m4.translate(mat, [
Math.sin(time / 1200),
Math.sin(time / 1300),
Math.sin(time / 1400) - 1.8,
], mat);
m4.rotateX(mat, time / 1000, mat);
m4.rotateY(mat, time / 1100, mat);
// calls gl.uniform
twgl.setUniforms(prgInfo, {
matrix: mat,
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
这可以说会更有效,因为我们不是一次拒绝 1000 多个像素,而是拒绝整个三角形,甚至跳过每个像素的检查。但它需要更多数据,因为我们需要能够同时将每个三角形的所有 3 个顶点传递给顶点着色器。
注意:如果您是 WebGL 的新手,您可能会发现 these articles 有用。
我是 WEBGL 的新手,我阅读了很多关于片段着色器的帖子。但是,我不知道如何访问片段着色器中的原始顶点(或每个顶点的标志)。
我的目标是在片段的至少一个原始顶点被剪裁后丢弃该片段。
感谢帮助
您不能直接访问片段着色器中的顶点。默认情况下,片段着色器获取的唯一数据是屏幕坐标(canvas/framebuffer 坐标)和当前光栅化像素的深度缓冲区值。您必须传递的所有其他数据。
最重要的是,在顶点着色器中,您可以计算顶点是否被剪裁并将该信息作为变量传递给片段着色器。如果没有被裁剪,你将传递 0,如果被裁剪,你将传递 1。 varyings 得到插值,因此如果在片段着色器中 varying 大于 0,则其中一个顶点被剪裁。
const vs = `
attribute vec4 position;
uniform mat4 matrix;
varying float clipped;
void main() {
gl_Position = matrix * position;
clipped = (
any(lessThan(gl_Position.xyz, -gl_Position.www)) ||
any(greaterThan(gl_Position.xyz, gl_Position.www))
) ? 1.0
: 0.0;
}
`;
const fs = `
precision highp float;
varying float clipped;
void main() {
if (clipped > 0.0) {
discard;
}
gl_FragColor = gl_FrontFacing ? vec4(1, 0, 0, 1) : vec4(0, 0, 1, 1);
}
`;
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
// compile shaders, link program, look up locations
const prgInfo = twgl.createProgramInfo(gl, [vs, fs]);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each of
// position, normals, texcoord, indices of a sphere.
const bufferInfo = twgl.primitives.createSphereBufferInfo(gl, 1, 8, 8);
function render(time) {
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.enable(gl.DEPTH_TEST);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer for each attribute
twgl.setBuffersAndAttributes(gl, prgInfo, bufferInfo);
gl.useProgram(prgInfo.program);
const fov = 60 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const near = 0.1;
const far = 5.0;
const mat = m4.perspective(fov, aspect, near, far);
m4.translate(mat, [
Math.sin(time / 1200),
Math.sin(time / 1300),
Math.sin(time / 1400) - 1.8,
], mat);
m4.rotateX(mat, time / 1000, mat);
m4.rotateY(mat, time / 1100, mat);
// calls gl.uniform
twgl.setUniforms(prgInfo, {
matrix: mat,
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
可能有更有效的方法,比如将三角形的所有顶点传递给顶点着色器,如果其中任何一个被剪裁,则设置所有顶点被剪裁,这样三角形就不会被栅格化。
const vs = `
attribute vec4 position;
attribute vec4 position1; // second vertex for triangle
attribute vec4 position2; // third vertex for triangle
uniform mat4 matrix;
bool vertexClipped(vec4 clipspace) {
return any(lessThan(clipspace.xyz, -clipspace.www)) ||
any(greaterThan(clipspace.xyz, clipspace.www));
}
void main() {
gl_Position = matrix * position;
vec4 clipPosition1 = matrix * position1;
vec4 clipPosition2 = matrix * position2;
bool clipped = vertexClipped(gl_Position) ||
vertexClipped(clipPosition1) ||
vertexClipped(clipPosition2);
if (clipped) {
gl_Position = vec4(vec3(2), 1); // some offscreen value
}
}
`;
const fs = `
precision highp float;
void main() {
gl_FragColor = gl_FrontFacing ? vec4(1, 0, 0, 1) : vec4(0, 0, 1, 1);
}
`;
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
// compile shaders, link program, look up locations
const prgInfo = twgl.createProgramInfo(gl, [vs, fs]);
const verts = twgl.primitives.deindexVertices(twgl.primitives.createSphereVertices(1, 8, 8));
// copy the positions
const position1 = new Float32Array(verts.position);
const position2 = new Float32Array(verts.position);
// shift the positions so we can pass all 3 vertices for each triangle
// to the vertex shader for each iteration
for (let i = 0; i < position1.length; i += 9) {
{
// 0, 1, 2 => 1, 2, 0
const temp = position1.slice(i, i + 3);
position1.set(position1.slice(i + 3, i + 9), i);
position1.set(temp, i + 6);
}
{
// 0, 1, 2 => 2, 0, 1
const temp = position2.slice(i + 6, i + 9);
position2.set(position2.slice(i + 0, i + 6), i + 3);
position2.set(temp, i);
}
}
verts.position1 = position1;
verts.position2 = position2;
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each of
// position, normals, texcoord, indices of a sphere.
const bufferInfo = twgl.createBufferInfoFromArrays(gl, verts);
function render(time) {
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.enable(gl.DEPTH_TEST);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer for each attribute
twgl.setBuffersAndAttributes(gl, prgInfo, bufferInfo);
gl.useProgram(prgInfo.program);
const fov = 60 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const near = 0.1;
const far = 5.0;
const mat = m4.perspective(fov, aspect, near, far);
m4.translate(mat, [
Math.sin(time / 1200),
Math.sin(time / 1300),
Math.sin(time / 1400) - 1.8,
], mat);
m4.rotateX(mat, time / 1000, mat);
m4.rotateY(mat, time / 1100, mat);
// calls gl.uniform
twgl.setUniforms(prgInfo, {
matrix: mat,
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
这可以说会更有效,因为我们不是一次拒绝 1000 多个像素,而是拒绝整个三角形,甚至跳过每个像素的检查。但它需要更多数据,因为我们需要能够同时将每个三角形的所有 3 个顶点传递给顶点着色器。
注意:如果您是 WebGL 的新手,您可能会发现 these articles 有用。