Canvas and alpha
Forum rules
Before you make a thread asking for help, read this.
Before you make a thread asking for help, read this.
Canvas and alpha
Hi all!
I have some troubles understanding how alpha and canvases work together:
I'm drawing a texture with some transparency on a canvas and then drawing the canvas itself on the screen. I've noticed that, if i don't use the canvas and i draw directly the texture on screen, i obtain a different effect. By searching the forums i've found out that i need to set the blend mode to "premultiplied" before drawing the canvas, and it works as expected.
The problem occurs when i need to apply an additional transparency on what i draw (by using a shader or by changing the color with love.graphics.setColor(1, 1, 1, myAlpha)). Even with premultiplied blend mode i can't get what i need.
Am i missing something?
You can find a minimal .love file in the attachments that reproduces the problem.
EDIT: I have no control over what is drawn inside the canvas, so let's say i can use setColor only before drawing the canvas on the screen.
I have some troubles understanding how alpha and canvases work together:
I'm drawing a texture with some transparency on a canvas and then drawing the canvas itself on the screen. I've noticed that, if i don't use the canvas and i draw directly the texture on screen, i obtain a different effect. By searching the forums i've found out that i need to set the blend mode to "premultiplied" before drawing the canvas, and it works as expected.
The problem occurs when i need to apply an additional transparency on what i draw (by using a shader or by changing the color with love.graphics.setColor(1, 1, 1, myAlpha)). Even with premultiplied blend mode i can't get what i need.
Am i missing something?
You can find a minimal .love file in the attachments that reproduces the problem.
EDIT: I have no control over what is drawn inside the canvas, so let's say i can use setColor only before drawing the canvas on the screen.
- Attachments
-
- Canvas.love
- (1.01 KiB) Downloaded 168 times
Re: Canvas and alpha
Thanks for the test case. The problem is that the default shader is not designed for applying alpha in premultiplied mode. You can try using this shader when drawing the canvas to the screen in premultiplied mode with extra alpha:
Edit: Changed 1 to 1.0 which is more compatible.
Code: Select all
local premult_shader = love.graphics.newShader
[[
vec4 effect(vec4 colour, Image tex, vec2 texpos, vec2 scrpos)
{
return colour.a * vec4(colour.rgb, 1.0) * Texel(tex, texpos);
}
]]
Re: Canvas and alpha
Or, if you can, use a shader that doesn't premultiply alpha when drawing to the Canvas. Which should be the default anyway imo, because these stupid special cases are a pain to deal with. It sucks.
Code: Select all
// use this instead of no shader to disable premultiplication
vec4 effect(vec4 col, Image tex, vec2 uv, vec2 fc) {
return col * Texel(tex, uv);
}
Re: Canvas and alpha
That's equivalent to the default shader, isn't it? Have you tried it in Tabaqui's example?grump wrote: ↑Fri Mar 19, 2021 1:35 pm Or, if you can, use a shader that doesn't premultiply alpha when drawing to the Canvas. Which should be the default anyway imo, because these stupid special cases are a pain to deal with. It sucks.
Code: Select all
// use this instead of no shader to disable premultiplication vec4 effect(vec4 col, Image tex, vec2 uv, vec2 fc) { return col * Texel(tex, uv); }
Yes, it's a pain and it sucks. But that's OpenGL. The real alpha compositing formula is not linear, and OpenGL can only perform compositing using linear formulas. However, if you start with a canvas initialized to R,G,B,A=0,0,0,0, and apply normal blending to draw on it, then draw the canvas to the screen using premultiplied alpha mode, the result is the same as if using true alpha compositing to draw to the canvas, and then true alpha compositing to draw the canvas to the screen, so that's one way we have to work around that OpenGL limitation.
Edit: The alternative is drawing always to the canvas using a shader for true alpha compositing, and the shader is more complex than the one you've posted:
Code: Select all
extern Image canvas;
vec4 effect(vec4 colour, Image tex, vec2 texpos, vec2 scrpos)
{
vec4 src = colour * Texel(tex, texpos);
vec4 dst = Texel(canvas, scrpos);
vec4 res;
res.a = src.a + dst.a * (1.0 - src.a);
res.rgb = (src.rgb * src.a + dst.rgb * dst.a * (1.0 - src.a)) / res.a;
return res;
}
Edit 2: A google search for 'alpha compositing with OpenGL' leads to this post: https://apoorvaj.io/alpha-compositing-o ... ied-alpha/ which is quite interesting and presents a shader that is equivalent to the above. It also explains why it works like that from the hardware perspective. Quite enlightening.
Re: Canvas and alpha
Uhm, what the hell. I must've been thinking about another engine or use case. Sorry about that. I could've sworn the rgb multiplication is done in the shader when using Canvas.
Anyway, for this exact copy-texture-to-canvas case you can use blendmode 'none' (or is it 'replace'?) to copy the color channels, but that's obviously not a general solution for more complex cases.
Anyway, for this exact copy-texture-to-canvas case you can use blendmode 'none' (or is it 'replace'?) to copy the color channels, but that's obviously not a general solution for more complex cases.
Re: Canvas and alpha
Thank you guys, pgimeno solution works as expected although i can't figure out why. Inside my mind the canvas should have been an exact copy of the texture after drawing inside it, so i can't really understand what was the problem. I guess i should study the topic more because i don't like to copypaste without fully understanding what the code does.
I've thought using the "replace" blend mode when drawing to the canvas but it won't work in the real project because i'm drawing multiple stuff in it.
I've thought using the "replace" blend mode when drawing to the canvas but it won't work in the real project because i'm drawing multiple stuff in it.
Re: Canvas and alpha
When you draw something in mode alpha + alphamultiply, this formula is applied:
Code: Select all
dst.rgb = src.rgb * src.a + dst.rgb * (1 - src.a)
If you draw the result of this with the same formula, the colors become even darker. alpha + premultiplied does this instead:
Code: Select all
dst.rgb = src.rgb + dst.rgb * (1 - src.a)
As for the setColor problem, this seems to be a quirk with how vertex colors are handled in premultiplied mode, but I don't want to get the details wrong again so I'll leave that to pgimeno.
Re: Canvas and alpha
Yeah, premultiplied alpha means that colours are darkened proportionally to the alpha. This means that if more alpha needs to be applied, more darkening is necessary, but the default shader doesn't do that; the formula to do it is the one in the shader I gave. In fact, maybe Löve should take into account the current alpha mode in the shader, to decide whether to apply the current formula or the premultiplied one.
- slime
- Solid Snayke
- Posts: 3132
- Joined: Mon Aug 23, 2010 6:45 am
- Location: Nova Scotia, Canada
- Contact:
Re: Canvas and alpha
The premultiplied alpha blend mode expects premultiplied colour inputs (textures, global color, etc). The global color used in the default shader comes from love.graphics.setColor. The default shader supports all blend modes, but only when the inputs to it match the blend mode. It's only when the inputs don't match the blend mode that custom shader logic is needed.
In other words, when using the default shader with the premultiplied alpha blend mode (provided you actually are drawing a texture which has premultiplied alpha in its pixels), you'll need to premultiply the global color's alpha when calling love.graphics.setColor.
In other words, when using the default shader with the premultiplied alpha blend mode (provided you actually are drawing a texture which has premultiplied alpha in its pixels), you'll need to premultiply the global color's alpha when calling love.graphics.setColor.
Code: Select all
love.graphics.setColor(red * alpha, green * alpha, blue * alpha, alpha)
Re: Canvas and alpha
Well, yes, premultiplying the alpha when calling setColor would obtain the same result. Is there a use case for having alpha blending mode = premultiplied, a setColor call with alpha < 1, and a colour passed to setColor that does not have premultiplied alpha? Premultiplying alpha is something I'd expect Löve to do automatically to the colour, when the alpha mode is premultiplied. This thread is about the confusion that Löve caused to Tabaqui for not having that automatically done for them.
Edit: Well, maybe there's one. Mode = replace / premultiplied sets the colour. It's not so clear cut, it seems.
Come to think about it, it doesn't even have to be a custom shader; just a preprocessing of the colour when Löve sends it to the shader internally would suffice.
Edit: Well, maybe there's one. Mode = replace / premultiplied sets the colour. It's not so clear cut, it seems.
Come to think about it, it doesn't even have to be a custom shader; just a preprocessing of the colour when Löve sends it to the shader internally would suffice.
Who is online
Users browsing this forum: Ahrefs [Bot], Bing [Bot], Google [Bot] and 243 guests