【问题标题】:Clipping in WEBGL fragment shader according primitive vertices根据原始顶点在 WEBGL 片段着色器中进行裁剪
【发布时间】:2019-12-17 11:26:36
【问题描述】:

我是 WEBGL 的新手,我阅读了很多关于片段着色器的帖子。但是,我无法弄清楚如何在片段着色器中访问原始顶点(或每个顶点的标志)。

如果片段的至少一个原始顶点被剪裁,我的目标是丢弃片段。

感谢您的帮助

【问题讨论】:

    标签: webgl fragment-shader


    【解决方案1】:

    您不能直接访问片段着色器中的顶点。默认情况下,片段着色器获取的唯一数据是屏幕坐标(画布/帧缓冲区坐标)和当前光栅化像素的深度缓冲区值。您必须传入的所有其他数据。

    在我的脑海中,在顶点着色器中,您可以计算一个顶点是否被剪裁,并将该信息作为变量传递给片段着色器。如果没有被剪裁,你会传递 0,如果被剪裁,你会传递 1。变量会被插值,所以如果在片段着色器中变量 > 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 很有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-06
      • 2014-08-17
      • 2019-01-24
      • 2011-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多