场景元素和背景之间的 WebGL Alpha 混合

WebGL Alpha blending between scene elements and background

我正在尝试弄清楚 WebGL 中的 alpha 混合,几乎就可以了,但需要一些洞察力。

我读过这个问题 and a few articles on the topic like this one David Guan: Alpha Blending and WebGL,其中讨论了混合模式、预乘 alpha 和片段着色器,但我不太明白。

这是我目前得到的:

方块混合到背景上但不相互混合。

红框的上边缘也有黑色边框。

有人有什么想法吗?

function drawHtml5() {
  var html5 = document.getElementById("html5Canvas").getContext("2d");
  html5.fillStyle = "rgba(255,0,0,0.5)";
  html5.fillRect(50,20,200,75);
  html5.fillStyle = "rgba(0,255,0,0.5)";
  html5.fillRect(100,50,175,75);
  html5.fillStyle = "rgba(0,0,255,0.5)";
  html5.fillRect(30,75,175,70);
}

function drawWebGL() {
  var gl = document.getElementById("canvas").getContext("webgl", {
    premultipliedAlpha: false,
  });
  gl.enable(gl.BLEND);
  gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

  var vertices = [
    -0.5,0.5,0.0,
    -0.5,-0.5,0.0,
    0.5,-0.5,0.0,
    0.5,0.5,0.0 
  ];

  indices = [3,2,1,3,1,0];

  // Create an empty buffer object to store vertex buffer
  var vertex_buffer = gl.createBuffer();

  // Bind appropriate array buffer to it
  gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

  // Pass the vertex data to the buffer
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

  // Unbind the buffer
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  // Create an empty buffer object to store Index buffer
  var Index_Buffer = gl.createBuffer();

  // Bind appropriate array buffer to it
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);

  // Pass the vertex data to the buffer
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

  // Unbind the buffer
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

  /*====================== Shaders =======================*/

  // Vertex shader source code
  var vertCode =
      'attribute vec3 coordinates;' +
      'uniform vec4 translation;'+
      'void main(void) {' +
      ' gl_Position = vec4(coordinates, 1) + translation;' +      
      '}';

  // Create a vertex shader object
  var vertShader = gl.createShader(gl.VERTEX_SHADER);

  // Attach vertex shader source code
  gl.shaderSource(vertShader, vertCode);

  // Compile the vertex shader
  gl.compileShader(vertShader);

  // Fragment shader source code
  var fragCode =
      'precision mediump float;' +
      'uniform vec4 u_fragColor;' + 
      'void main(void) {' +
      ' gl_FragColor = u_fragColor;' +
      //' gl_FragColor.rgb *= u_fragColor.a;' +
      '}';

  // Create fragment shader object 
  var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

  // Attach fragment shader source code
  gl.shaderSource(fragShader, fragCode);

  // Compile the fragmentt shader
  gl.compileShader(fragShader);

  // Create a shader program object to
  // store the combined shader program
  var shaderProgram = gl.createProgram();

  // Attach a vertex shader
  gl.attachShader(shaderProgram, vertShader);

  // Attach a fragment shader
  gl.attachShader(shaderProgram, fragShader);

  // Link both the programs
  gl.linkProgram(shaderProgram);

  // Use the combined shader program object
  gl.useProgram(shaderProgram);

  /* ======= Associating shaders to buffer objects =======*/

  // Bind vertex buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

  // Bind index buffer object
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer); 

  // Get the attribute location
  var coord = gl.getAttribLocation(shaderProgram, "coordinates");

  // Point an attribute to the currently bound VBO
  gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);

  // Enable the attribute
  gl.enableVertexAttribArray(coord);

  /*============= Drawing the Quad ================*/

  // Clear the canvas
  gl.clearColor(0.0, 0.0, 0.0, 0.0);

  // Enable the depth test
  gl.enable(gl.DEPTH_TEST);

  // Clear the color buffer bit
  gl.clear(gl.COLOR_BUFFER_BIT);

  // Set the view port
  gl.viewport(0,0,canvas.width,canvas.height);

    // Draw red box
  // 
    var u_FragColor = gl.getUniformLocation(shaderProgram, 'u_fragColor'); 
  gl.uniform4f(u_FragColor, 1,0,0,0.5);
  
    var translation = gl.getUniformLocation(shaderProgram, 'translation');
  gl.uniform4f(translation, 0, 0, 0, 0);
         
  gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
  
  // Draw green box
  // 
    var u_FragColor = gl.getUniformLocation(shaderProgram, 'u_fragColor'); 
  gl.uniform4f(u_FragColor, 0,1,0,0.5);
  
    var translation = gl.getUniformLocation(shaderProgram, 'translation');
  gl.uniform4f(translation, 0.2, -0.2, 0, 0);
  
  gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
  
  // Draw Blue box
  // 
    var u_FragColor = gl.getUniformLocation(shaderProgram, 'u_fragColor'); 
  gl.uniform4f(u_FragColor, 0,0,1,0.5);
  
    var translation = gl.getUniformLocation(shaderProgram, 'translation');
  gl.uniform4f(translation, -0.2, -0.4, 0, 0);
         
  gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

}

drawHtml5();
drawWebGL();
div {
  background-color: purple;
  background-image: linear-gradient(45deg, rgba(255, 255, 255, 1) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 1) 50%, rgba(255, 255, 255, 1) 75%, transparent 75%, transparent);
  background-size: 50px 50px;
  min-height: 600px;
  padding: 50px;
}

canvas {  
  border: 3px solid red;
  height: 300px;
  width: 400px;
}

p {
  color: red;
  font-family: "Arial";
  font-weight: "Bold";
  font-size: 2em;
  background: white;  
}
<div>
  <p>
    WebGL Canvas
  </p>
  <canvas id="canvas"></canvas>
  <p>
    HTML5 Canvas (Expected output)
  </p>
  <canvas id="html5Canvas"></canvas>
</div>

Link 到 JS Fiddle

如果你想要一个现场游乐场,请查看下面的 JS Fiddle

https://jsfiddle.net/scichart/wr6Lny4x/

我在 LinkedIn 上看到了你的 post。在这里也张贴我的答案以提高认识。

这是包含所有更改的 jsfiddle 的 link:https://jsfiddle.net/uom3ckqy/1/

实际的 alpha 混合在第 78 行的片段着色器中被禁用。尝试取消注释,这样片段着色器现在是:

// Fragment shader source code
  var fragCode =
      'precision mediump float;' +
      'uniform vec4 u_fragColor;' + 
      'void main(void) {' +
      ' gl_FragColor = u_fragColor;' +
      ' gl_FragColor.rgb *= u_fragColor.a;' +
      '}';

您现在可以启用 premultipliedAlpha 参数,方法是将其设置为 true,将 gl 变量保留为:

var gl = document.getElementById("canvas").getContext("webgl", {
    premultipliedAlpha: true,
  });

我用你的 jsfiddle 试过了,现在看起来像这样:

我还注意到 WebGL 绘制的四边形有点模糊。我不是该领域的专家,但似乎您需要在 HTML 元素的属性和 CSS 或 JS 中设置 canvas 的尺寸。这些答案更详细:WebGL: Everything is blurry despite using same code and Canvas width and height in HTML5

我更改了 canvas 以包含 widthheight 属性。将这些设置为与 CSS 中的相同,包含 WebGL 元素的 canvas 现在是:

  <canvas id="canvas" height='300' width='400'></canvas>

canvas 现在看起来符合预期: