3

I'm trying to get a simple effect to display using WebGL. Naturally, this means I'm using the fragment shader language defined in the GLSL ES 1.0 specification.

The code I am working with is largely copied from other sources. It sets up a square and uses the fragment and vertex shaders to determine the pixel colors. The following code will just display a white square.

gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);

However, if I change the alpha component to 1.0 then it will show a black square instead.

gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); // displays a black square

I'm assuming that the color that is output by the fragment shader must be combined with some previous color. How do I make sure that only the last color (regardless of its alpha value) is what is actually chosen as the color to be displayed?

Or perhaps I've got the order the wrong way around. Perhaps there is a later phase that is combining with the color from the fragment shader to produce the white color. In any case, I know that some kind of blending is going on because when I change the alpha value to 0.5 I get a grey square. I just want to know, where is the white color coming from? And how do I get rid of it?

As far as I can tell the problem is not to do with the blending function. The code is on GitHub here. Try it out in Google Chrome or Firefox.

Sean Seefried
  • 1,189
  • 7
  • 17

3 Answers3

5

The white color is not coming from any WebGL blending operations. It happens because canvas DOM elements are composited with the web page they reside in. Setting the background color of the canvas DOM element to black fixes the problem. This phenomenon has been written about here. However, the most definitive answer comes from the WebGL specification. The behaviour of compositing can be defined by setting various attributes of the WebGLContextAttributes object. Quoting from the specification:

The optional WebGLContextAttributes object may be used to change whether or not the buffers are defined. It can also be used to define whether the color buffer will include an alpha channel. If defined, the alpha channel is used by the HTML compositor to combine the color buffer with the rest of the page.

Thus, an alternative solution, that does not involve setting the background of the canvas to black, is to ask for the WebGL context with the following Javascript code

gl = canvas.getContext("experimental-webgl", { alpha: false } );
Sean Seefried
  • 1,189
  • 7
  • 17
  • Nice! +1 for persisting and drilling down on this. Ironically, I'm currently exploiting 2D canvas alpha to create an overlay for a 3D canvas, but somehow it did not occur to me that alpha on the 3D canvas would also interact with the DOM element behind. – brainjam May 31 '11 at 12:38
4

It would be helpful if you posted code, but it seems very much like you are blending your fragment color with a white background.

A possible culprit would be some WebGL initialization code that looks like:

    gl.clearColor(1.0, 1.0, 1.0, 1.0);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    gl.enable(gl.BLEND);

which initializes the color buffer clear color to white and enables alpha blending.

So to get rid of your problem, just don't enable blending. In other words, lose the gl.enable(gl.BLEND); statement.

Update: Now that you've posted your code, it sounds like your problem is the opposite of what I was speculating. Assuming that you want the black background to show through according to the alpha that you set, you should replace your gl.disable(gl.BLEND) with

    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    gl.enable(gl.BLEND);
brainjam
  • 18,180
  • 7
  • 49
  • 78
  • I've upvoted this answer, since it's the best so far. But this doesn't seem to be the problem. I have put my code up on GitHub [here](https://sseefried@github.com/sseefried/webgl-stackoverflow.git). Most of the code is not even mine, but you can see that I've disabled blending and I still get the problem. Just open test.html in Google Chrome to see the problem. – Sean Seefried May 30 '11 at 07:10
  • 1
    @Sean No, rockerest's answer is the best so far, as it explains exactly what is going on! – Christian Rau May 30 '11 at 12:11
  • @brainjam. I've tried adding those two lines and I get an even darker grey square which makes me think that there are two blends going on. My main question is "where is the white coming from". Nowhere in the code do I specify that anything should be white. – Sean Seefried May 31 '11 at 00:33
  • @Sean, at this point you are blending against the black background. There should no longer be any white in the blending equation. It looks like when there is no blending enabled (which was the case earlier), there is a default blend against white. But now that you have enabled blending, it should be a "true" blend against the background. Try playing with the clearColor() statement and use colors other than (0.0,0.0,0.0) and see how the blending operates. Hope this helps. I assume that if you're using alpha you actually want some blending to occur. – brainjam May 31 '11 at 02:22
  • @brainjam. I found out what the problem was. Thank you so much for spurring me on to find the definitive answer. – Sean Seefried May 31 '11 at 03:28
1

I know NOTHING about this topic (glsl, webgl, etc.), but you said alpha which makes me think that's an RGBA value. So a 0.0, 0.0, 0.0, 0.0 is black, with 0.0 alpha coverage. In other words, totally transparent.

0.0, 0.0, 0.0, 1.0 is black with 1.0 alpha coverage. In other words, totally opaque. It sounds like the output is 100% correct.

Let me clarify, just in case the above wasn't clear (it seemed confusing to me).

The last number is just affecting the transparency of the color, the first three numbers are affecting the Red, Green, and Blue respectively.

I'm not sure of their max/min values, but in web browsers (my area of knowledge), the colors go from 0 to 255. Where 0 is none of that color, and 255 is all of that color.

So in your case you are requesting a square with 0 red, 0 green, and 0 blue (which is just black). However, when you set the transparency (the alpha channel) to zero, it renders as white (or more likely - transparent, and whatever is behind it is white). When you set the alpha transparency to .5 it renders Black (like 0,0,0 should) but only half visible. When you set the alpha transparency to 1.0, it renders a fully opaque, black square.

Make sense?

rockerest
  • 9,847
  • 3
  • 33
  • 67
  • Thanks for you very clear explanation but, unfortunately, I'm aware of the basics of RGBA already. This is a very specific question about how the graphics pipeline works in a WebGL program using GLSL. I realise that I'm setting the alpha channel of my color to 0.0 which means that if it is blended with some other color else it will contribute nothing. I just want to know what it's being blended with in my WebGL program. I just don't know! – Sean Seefried May 29 '11 at 00:39
  • 2
    @Sean It's probably blended with your white background. And I assume the WebGL pipeline works exactly like the desktop GL pipeline (perhaps with some limitations, but these won't apply to your case). – Christian Rau May 30 '11 at 12:10
  • @Christian Rau. You were right it was blended with the white background. When I first read your comment I didn't realise you meant the canvas background. – Sean Seefried May 31 '11 at 03:29
  • Downvote on the answer that successfully enumerates the problem ("transparent, and whatever is behind it is white") and over a year later. I'm often amazed at people. – rockerest Jul 16 '12 at 18:56