Fragment shader: assigning vs multiplying COLOR component

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By mkx

Hi, I am new to Godot and game programming in general.

I am trying to write a simple CanvasItem shader in which I want to manipulate the alpha channel of a pixel. The CanvasItem to which the shader material is attached is a Label that renders text using a bitmap font.

My first intuition reading the tutorial was to simply set the alpha component like so:

shader_type canvas_item;

void fragment() {
   COLOR = texture(TEXTURE, UV);
   COLOR.a = 0.5; // Set to 50% transparency
}

But that does not work; it results in all font glyphs being rendered as white boxes, so it seems some of the texture information is lost?

Through trial and error, I found that multiplying the vector component with itself has the desired effect:

shader_type canvas_item;

void fragment() {
	COLOR = texture(TEXTURE, UV);
	COLOR.a *= 0.5; // Half the current transparency.
}

Note the *= vs =.

I don’t understand why that is; why can’t I just set a color component to the desired value without destroying information in other color channels?

I’m beginning to think that an expression like v.a = y is interpreted by the shader compiler as v = vec4(1, 1, 1, y), is that so? That would also explain why v.a *= y works because v = v * vec4(1, 1, 1, y) would not change the remaining color components?

mkx | 2023-02-05 10:19

:bust_in_silhouette: Reply From: zhyrin

The shader dosn’t just run for where your letters are, it runs for the the entire bounding box of the label - the empty space around them too.
If you write COLOR.a = 0.5, you set 50% opacity even to pixels that were transparent before.
Here in the first line you get the current pixel’s color (including alpha) and by multiplying the alpha, you tell how much of the pixels alpha value you want to write to the outcome.
With a mutiplication of 0.0 you set the alpha to zero, with a multiplication of 1.0 you leave the alpha as is, and with a multiplication of 0.5 you only use 50% of the original value.
In the case of tranparent pixels, 50% of 0 is still 0, in the case of fully opaque pixels (1.0 alpha), 50% of 1 is 0.5, the pixel will be half transparent.

Thank you, this explains a lot! I also learned meanwhile on Shader Basics - Fragment Shader | GPU Shader Tutorial that fragment != pixel (I did some DirectX programming back in the early 2000s and they just called them pixel shaders, so this confused me at first.)

What is still not clear to me: so the shader runs for the entire BBOX. The letters had a color of #FFFF (opaque white), the label background itself is #0000 (transparent) and the node behind the label had a color value of #000F (all black). Why would assigning an alpha value of 0.5f to the transparent areas of the label also color these areas white? I would expect that they would be blended with the black background to 50% black i.e. some shade of grey. That is not what’s happening.

I feel like I’m still missing something about how these pixels (or fragments) compose.

mkx | 2023-02-11 08:39

This is the part when I can’t give concrete information, only speculation, since I don’t know how shaders render text.
I assume the entire bounding box is painter with the font’s color, and the shape of the letters is used as an alpha mask. Try changing the font color and seeing if the supposedly transparent pixels become differently colored, not white.

zhyrin | 2023-02-11 10:12

That makes sense. I will play around with it more, thanks.

mkx | 2023-02-12 08:45