不要在webgl中混合已经与自身交叉线的折线

时间:2019-09-30 13:17:33

标签: webgl alphablending webgl2

我在启用了混合功能的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是代码笔示例。

左边的线与自身交叉,似乎与自身融合,因此它变得更暗。

我希望折线在这些折线之间起作用,但不希望折线与其自身混合。有办法吗?

1 个答案:

答案 0 :(得分:1)

一种方法是使用模板测试。您将设置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>