floooh / sokol

minimal cross-platform standalone C headers
https://floooh.github.io/sokol-html5
zlib License
6.53k stars 467 forks source link

Offscreen with alpha background. Is it possible? #1064

Closed eduardodoria closed 2 weeks ago

eduardodoria commented 2 weeks ago

I`m trying to make an 3D UI using render-to-texture, but I cannot do with transparent background.

This is the result without transparency: Captura de tela de 2024-06-17 16-33-34

After doing alpha 0.5 in clear_value pass_action.colors[0].clear_value = { 0.8, 0.8, 0.8, 0.5 }: Captura de tela de 2024-06-17 16-34-04

Now, after doing in RTT pipeline pip_rtt_desc.colors[0].write_mask = SG_COLORMASK_RGBA: Captura de tela de 2024-06-17 16-35-01

The last one is almost result I want, but transparent UIs (text) is getting wrong background even if drawed last than others.

floooh commented 2 weeks ago

I wouldn't mess around with the pipeline object write-mask (for an exception, see below), but instead do everything with alpha-blending, and make sure that the alpha values of the render target texture have the transparency you need for rendering that texture into the default pass.

.color[0] = {
    .blend = {
        .enabled = true,
        .src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA,
        .dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA
        .src_factor_alpha = SG_BLENDFACTOR_ZERO,
        .dst_factor_alpha = SG_BLENDFACTOR_ONE,
    },
},

...that way your text should be properly alpha-blended on top of the UI element backgrounds, but the existing alpha values in the render target texture are not modified by the text rendering (e.g. window background stay at alpha 1.0)

Alternatively you could also disable writing alpha values via the write-mask, but only when rendering text:

.color[0] = {
    .write_mask = SG_COLORMASK_RGB,
    .blend = {
        .enabled = true,
        .src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA,
        .dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA
    },
},

...this should have the same effect of not changing the render target alpha values when rendering text via alpha-blending into the render target.

Finally, render the render target as texture with alpha blending into your default pass (like you already do).

You can also check if the render target's alpha channel looks correct by integrating sokol_gfx_imgui.h. In the image viewer window, the textures are rendered with alpha-blending, and the Dear ImGui UI itself is also rendered with alpha-blending (not the slightly transparent background):

image

eduardodoria commented 2 weeks ago

Thank you. I got the results I want but with little different settings.

  • I would first clear the render target to alpha value 0.0 instead of 0.5

I don't understant why you said this because I don't want full transparent background, so I keep aplha with 0.2.

        .src_factor_alpha = SG_BLENDFACTOR_ZERO,
        .dst_factor_alpha = SG_BLENDFACTOR_ONE,

If I use this settings the result is the same: Captura de tela de 2024-06-18 09-34-04

But I changed to this:

        .src_factor_alpha = SG_BLENDFACTOR_ONE,
        .dst_factor_alpha = SG_BLENDFACTOR_ONE,

And now I get this result:

Captura de tela de 2024-06-18 09-33-21

floooh commented 2 weeks ago

Hmm ok, ONE/ONE basically adds the alpha from the window background pixel shader on top of the alpha from the render target, that works too and is even better if your window backgrounds should be semi-transparent (and maybe you can even use the same shader for the text then).

What I had in mind was to not use any alpha blending at all when rendering the window background into the render target, but instead directly write an alpha value of 1.0 (and only use the ZERO/ONE alpha blend factors for the text) - but yeah, your solution might indeed be better and simpler.

I'll close the ticket since it looks like the issue is resolved :)

eduardodoria commented 2 weeks ago

Based on this reference https://stackoverflow.com/a/18497511/6089705:

the FBO contains colour already multiplied with the alpha channel, so instead of doing that again with GL_SRC_ALPHA, it is drawn with GL_ONE. The destination colour is attenuated normally with GL_ONE_MINUS_SRC_ALPHA.

So, I think for better result I should blend alpha with:

        .src_factor_alpha = SG_BLENDFACTOR_ONE,
        .dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,