已经着色的像素上的 Webgl 片段着色器透明度

Webgl fragment shader transparency on pixel that has already been colored

前几天刚了解Webgl,对透明行为还是有点迷糊。我正在尝试使用片段着色器在三角形内绘制一些圆圈。在我有两个三角形重叠之前,一切似乎都正常。我的片段着色器确定该点是否在圆圈内,然后将颜色设置为 (1.,0.,0.,1.)(如果在圆圈内)或 (1.,0.,0., 0.) 如果不是。

我期望的行为是,当片段着色器将像素设置为 (1.,0.,0.,0.) 时,该像素已经被着色 (1.,0.,0.,1. ),它将显示为 (1.,0.,0.,1.)。但是,它看起来与背景颜色相同。
我尝试使用以下方法打开混合模式,但它似乎仍然不起作用:

gl.enable( gl.BLEND );
gl.blendEquation( gl.FUNC_ADD );
gl.blendFunc( gl.ONE_MINUS_CONSTANT_ALPHA, gl.ONE_MINUS_SRC_ALPHA );

我在下面附上了我的代码。

const vertexShader = `

attribute vec4 aPosition;
attribute vec4 aCenter;
varying vec4 pos;
varying vec4 center;

void main() {
    center=aCenter;
    pos=aPosition;
    gl_Position = aPosition;
}
`;


const fragmentShader = `
precision mediump float;

varying vec4 pos;
varying vec4 center;

void main() {
    float inside = pow(pos.x - center.x, 2.0) + pow(pos.y - center.y, 2.0);
    float val=max(sign(pow(center.z, 2.0)-inside),0.);
    vec4 color=vec4(1.,0.,0.,val);
    gl_FragColor = color;
}
`;


const DEG_TO_RAD = 0.0174532925;
const TRI_HEIGHT_MOD = 2;
// build simple circle
var points = [];
var centers = [];
function buildCircle(center, r) {
    let angles=[0,120,240];
    for(let k=0;k<angles.length;k++){
      centers.push(center[0]);
      centers.push(center[1]);
      centers.push(r);
      var x=r * TRI_HEIGHT_MOD * Math.cos(angles[k] * DEG_TO_RAD) + center[0];
      var y=r * TRI_HEIGHT_MOD * Math.sin(angles[k] * DEG_TO_RAD) + center[1];
      points.push(x);
      points.push(y);
    } 
}

buildCircle([-.3,0],.5);
buildCircle([0,0],.5);



function loadShadersFromString(gl,vertexSource,fragmentSource){
  // first compile the vertex shader
  var vertexShader = gl.createShader(gl.VERTEX_SHADER);
  gl.shaderSource(vertexShader,vertexSource);
  gl.compileShader(vertexShader);
  
  if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
          console.log(gl.getShaderInfoLog(vertexShader));
          return null;
      }
  
  // now compile the fragment shader
  var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
  gl.shaderSource(fragmentShader,fragmentSource);
  gl.compileShader(fragmentShader);
  
  if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
          console.log(gl.getShaderInfoLog(fragmentShader));
          return null;
      }

  // OK, we have a pair of shaders, we need to put them together
  // into a "shader program" object
  var shaderProgram = gl.createProgram();
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);

  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
    alert("Could not initialise shaders");
  }
  return shaderProgram;
}

function loadScene() {
  // Get A WebGL context
  /** @type {HTMLCanvasElement} */
  var canvas = document.querySelector("#canvas");
  var gl = canvas.getContext("webgl");
  if(!gl){return;}
  webglUtils.resizeCanvasToDisplaySize(gl.canvas);
  // gl.drawElements(gl.LINES, given_animal.vertex_indices_buffer.numItems, gl.UNSIGNED_SHORT, 0);
  // gl.lineWidth(width);

  // setup GLSL program
  // var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-3d", "fragment-shader-3d"]);
  var program = loadShadersFromString(gl,vertexShader,fragmentShader);

  // look up where the vertex data needs to go.
  var positionLocation = gl.getAttribLocation(program, "aPosition");
  var centerLocation = gl.getAttribLocation(program, "aCenter");

  // Create a buffer to put positions in
  var positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(points),gl.STATIC_DRAW);
  var centerBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, centerBuffer);
  gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(centers),gl.STATIC_DRAW);

  drawScene();
  function drawScene() {
    webglUtils.resizeCanvasToDisplaySize(gl.canvas);
    // Tell WebGL how to convert from clip space to pixels
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    // Clear the canvas AND the depth buffer.
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    // Turn on culling. By default backfacing triangles
    // will be culled.
    gl.enable(gl.CULL_FACE);
    // Enable the depth buffer
    gl.enable(gl.DEPTH_TEST);

    gl.enable( gl.BLEND );
    gl.blendEquation( gl.FUNC_ADD );
    gl.blendFunc( gl.ONE_MINUS_CONSTANT_ALPHA, gl.ONE_MINUS_SRC_ALPHA );

    // Tell it to use our program (pair of shaders)
    gl.useProgram(program);
    // Turn on the position attribute
    gl.enableVertexAttribArray(positionLocation);
    // Bind the position buffer.
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    // Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
    var size = 2;          // 3 components per iteration
    var type = gl.FLOAT;   // the data is 32bit floats
    var normalize = false; // don't normalize the data
    var stride = 0;        // 0 = move forward size * sizeof(type) each iteration to get the next position
    var offset = 0;        // start at the beginning of the buffer
    gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);

    gl.enableVertexAttribArray(centerLocation);
    gl.bindBuffer(gl.ARRAY_BUFFER, centerBuffer);
    // Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
    var size = 3;          // 3 components per iteration
    var type = gl.FLOAT;   // the data is 32bit floats
    var normalize = false; // don't normalize the data
    var stride = 0;        // 0 = move forward size * sizeof(type) each iteration to get the next position
    var offset = 0;        // start at the beginning of the buffer
    gl.vertexAttribPointer(centerLocation, size, type, normalize, stride, offset);


    // Draw the geometry.
    var primitiveType = gl.TRIANGLES;
    var offset = 0;
    var count = points.length/2;
    gl.drawArrays(primitiveType,offset,count);

    // Call drawScene again next frame
    requestAnimationFrame(drawScene);
  }
}

loadScene();
body {
  margin: 0;
    background-color: wheat;
}

#html{
    background-color: wheat;
}
canvas {
  margin: 0;
    position: absolute;
  width: 90vw;
  height: 90vh;
  left: 5vw;
  top: 5vh;
  display: block;
}
<canvas id="canvas"></canvas>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>

Blending can only work if the Depth Test 未启用:

gl.enable(gl.DEPTH_TEST);

启用深度测试后,深度测试可能会丢弃碎片,然后再进行混合。