I'm trying to figure out alpha blending in WebGL and almost there but need some insight.
I've read this question WebGL: How to correctly blend alpha channel png and a few articles on the topic like this one David Guan: Alpha Blending and WebGL which talk about the blend modes, premultiplying alpha and fragment shaders but can't quite figure this out.
Here's what I've got so far:
The squares blend onto the background but not onto each other.
Also the top edge of the red box has a dark border.
Anyone have any ideas?
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 to JS Fiddle
Check out the JS Fiddle below if you want a live playground
I saw your post on LinkedIn. Posting my answer here as well for awareness.
Heres a link to a jsfiddle containing all the changes: https://jsfiddle.net/uom3ckqy/1/
The actual alpha blending is disabled in the fragment shader on line 78. Try uncommenting it, so that the fragment shader now is:
// 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;' +
'}';
You can now enable the premultipliedAlpha
argument, by setting it to true
, leaving the gl
variable as:
var gl = document.getElementById("canvas").getContext("webgl", {
premultipliedAlpha: true,
});
I tried it with your jsfiddle, and it now looks like this:
I also noticed that the drawn quads by WebGL are a bit blurry. I am no expert on the field, but it seems like you need to set the dimensions of the canvas
both in the HTML element's attributes and in either CSS or JS. These answers go into more detail: WebGL: Everything is blurry despite using same code and Canvas width and height in HTML5
I changed the canvas to include the width
and height
attributes. Setting these to be the same as in the CSS, The canvas containing the WebGL elements is now:
<canvas id="canvas" height='300' width='400'></canvas>
The canvas now looks as expected: