Skip to content

vello shaders: Refactor alpha inversion to handle faint alpha values#1432

Open
tgfrerer wants to merge 4 commits intolinebender:mainfrom
tgfrerer:fix-alpha-inversion-fine
Open

vello shaders: Refactor alpha inversion to handle faint alpha values#1432
tgfrerer wants to merge 4 commits intolinebender:mainfrom
tgfrerer:fix-alpha-inversion-fine

Conversation

@tgfrerer
Copy link

Tweak alpha inversion calculation to avoid artifacts with faint alpha values.

Contrary to MSAA16, Analytic area anti-aliasing does sometime return very faint alpha values.

This can create artifacts that look like ghosting when looking at just the RGB channels in isolation, because the RGB values get boosted out of proportion by a very low alpha value.

Usually, this is invisible, as it gets masked when drawing the final image using alpha blending, but it may be useful to correct for this at source.

Before the change: (Notice the artifacts in the RGB-only channel view (right))

image image

After the change: (Notice artifacts are corrected now in RGB-only channel view, while keeping same visual for RGBA)

image image

Update alpha inversion logic to avoid artifacts with faint alpha values.

Analytic area anti-aliasing does sometime return very faint alpha artifacts. This can create artifacts that look like ghosting when looking at just the RGB channels in isolation, because the RGB values get boosted out of proportion by the very low alpha value. 

Usually, this is masked when applying alpha blending, but it may be useful to correct for this at shource.
@tgfrerer tgfrerer changed the title Refactor alpha inversion to handle faint alpha values vello shaders: Refactor alpha inversion to handle faint alpha values Feb 11, 2026
catch very faint alpha values at the very end -- this has better stability for 32bit per channel framebuffers. Any pixel with an alpha value < 1e-6 gets squashed to `rgba(0,0,0,0)`.
Copy link
Contributor

@raphlinus raphlinus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems fine to me.

Because we're already doing the comparison with the threshold, I wonder whether the following code might be slightly more efficient. But I suspect it doesn't make any real difference.

    var rgba_sep = vec4(0.0);
    if fg.a > 1e-6 {
        let a_inv = 1.0 / fg.a;
        rgba_sep = vec4(fg.rgb * a_inv, fg.a);
    }

@tgfrerer
Copy link
Author

@raphlinus thank you for looking into this :) I like the form you suggest better -- it more clearly shows the intent of the code -- and i've updated the PR accordingly.

I think it is probably neutral in terms of performance (i've seen step() perform oddly less well than a plain if in the past, while step should give a driver more of an opportunity to minimize branching; but i doubt the difference would be measurable, and readability is ;)

I've also tested the newest version for good measure and it fixes the rgb artifacts just as well.
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments