Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add premult alpha blending to 3D (spatial) shaders #85609

Merged
merged 1 commit into from
May 1, 2024

Conversation

QbieShay
Copy link
Contributor

@QbieShay QbieShay commented Dec 1, 2023

@OhiraKyou
Copy link

OhiraKyou commented Dec 2, 2023

I suggest converting this PR to an implementation of a general-purpose DELAYED_RGB_MULTIPLIER built-in vec3, as described in my comment on the associated proposal issue. Most of the necessary code is the same (minus the automatic blend mode enabling, which would be omitted).

Example:

#ifdef DELAYED_RGB_MULTIPLIER_USED
    vec3 delayed_rgb_multiplier = vec3(1.0);
#endif // DELAYED_RGB_MULTIPLIER_USED

@QbieShay
Copy link
Contributor Author

@OhiraKyou could you give me an example usecase of this multiplier being a vec3? what kind of effect would it enable?

@OhiraKyou
Copy link

First, vec3 * float multiplication is per-component anyway. So, it's just good form to expose each component's multiplier to the user by making the multiplier a vec3 as well. That said, here are a couple of specific use cases.

Assumptions

The use cases described below assume the following:

  • The multiplier is generally available rather than coupled to the premultiplied alpha blend mode—which it really doesn't need to be coupled to.
  • The multiplier is applied after the sRGB to linear conversion and after built-in lighting.

Use cases

Custom channel mixing

Custom, per-channel post-processing of shader output colors—that includes lighting—may be applied in linear space by assigning a color multiplier to the delayed multiplier.

This includes processing of per-channel, non-color data for custom buffer rendering.

Custom colored lighting

Custom colored lighting (with no actual light objects in a scene) may be implemented in linear space in otherwise sRGB compatibility renderer shaders by ignoring the existing lighting system (i.e., using unshaded shaders) and assigning custom light output to the delayed multiplier.

@QbieShay
Copy link
Contributor Author

QbieShay commented Dec 14, 2023

We've discussed this PR in the rendering meeting and the design/thinking process around this is the following:

Like everything in Godot, we want to first talk about problems, then solutions.
The usecases you have provided are more along the lines of "if this was there, I could use it like this" rather than "I have this problem and I can't solve it without this".

Light multiplication is something I personally really want to do. I am hoping to be able to eventually have a post-light function (this is a wish, not a promise). But we need to respond with solution when there's a problem to begin with. But this is a whole different problem than what we're trying to solve here.
The problem to begin with here is having premultiplied alpha in the engine, so we should focus of solving that problem.

Additionally, concerns have been brought up about leaving doors open for usecases that are not explicitely supported: this may be a problem in the future when optimizing shaders or refactoring them. I understand that we could say "any use outside of premul alpha is unsupported and you're on your own if you do it" but this historically has never worked: people depend on it, and are disappointed when we "break" things that were never supposed to work to begin with.

I will bring this PR to completion in this dev cycle (4.3), unless something comes up, so that we can finally have premul alpha in 3D ^^ (integrating the render mode too)

@QbieShay QbieShay force-pushed the qbe/premul-builtin branch 2 times, most recently from 5a6fc8a to 25f6dbc Compare December 15, 2023 20:44
@QbieShay QbieShay marked this pull request as ready for review December 15, 2023 20:47
@QbieShay QbieShay requested review from a team as code owners December 15, 2023 20:47
Copy link
Member

@clayjohn clayjohn left a comment

Choose a reason for hiding this comment

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

Great work! I left a few comments to help clean this up. Most of them are just enforcing a consistent naming of BLEND_MODE_PREMULT_ALPHA

doc/classes/BaseMaterial3D.xml Outdated Show resolved Hide resolved
drivers/gles3/rasterizer_scene_gles3.cpp Outdated Show resolved Hide resolved
scene/resources/material.cpp Outdated Show resolved Hide resolved
drivers/gles3/storage/material_storage.cpp Outdated Show resolved Hide resolved
servers/rendering/shader_compiler.cpp Outdated Show resolved Hide resolved
servers/rendering/shader_compiler.cpp Outdated Show resolved Hide resolved
servers/rendering/shader_compiler.h Outdated Show resolved Hide resolved
servers/rendering/shader_compiler.h Outdated Show resolved Hide resolved
@mikejohnstn
Copy link

Any idea if PREMUL_ALPHA support is still targeting 4.3? I'm building a tool that depends on it, so it would help to know if I can aim to target a particular release.

clayjohn
clayjohn previously approved these changes Apr 30, 2024
Copy link
Member

@clayjohn clayjohn left a comment

Choose a reason for hiding this comment

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

Looks great!

doc/classes/BaseMaterial3D.xml Outdated Show resolved Hide resolved
@akien-mga
Copy link
Member

This PR uses a mix of PREMULT_ALPHA and premul_alpha spelling (note the presence or absence of T). This should be unified to use only one spelling.

@clayjohn clayjohn dismissed their stale review April 30, 2024 19:36

See akien-mga's comment after the approval

@QbieShay QbieShay force-pushed the qbe/premul-builtin branch 2 times, most recently from 2a0a535 to b87eeeb Compare April 30, 2024 22:52
@akien-mga akien-mga changed the title Add PREMUL_ALPHA builtin Add premult alpha blending to 3D (spatial) shaders Apr 30, 2024
@akien-mga akien-mga merged commit e950d7a into godotengine:master May 1, 2024
16 checks passed
@akien-mga
Copy link
Member

Thanks!

@@ -688,6 +688,9 @@ void BaseMaterial3D::_update_shader() {
case BLEND_MODE_MUL:
code += "blend_mul";
break;
case BLEND_MODE_PREMULT_ALPHA:
code += "blend_premul_alpha";
Copy link
Member

Choose a reason for hiding this comment

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

I merged a bit too fast, this one is still wrong.

Copy link
Member

Choose a reason for hiding this comment

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

So it seems like we had this BLEND_MODE_PREMULT_ALPHA but blend_premul_alpha inconsistency in CanvasItem shaders already.

Should we fix it in Spatial shaders and accept that we have an inconsistency in 2D blend_premul_alpha / 3D blend_premult_alpha, or should we do like in 2D?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I noticed that too, but I'd rather not start touching things elsewhere for now

Copy link
Member

@akien-mga akien-mga May 1, 2024

Choose a reason for hiding this comment

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

I mean this one is a bug, it's writing blend_premul_alpha in Spatial shaders but what's expected is blend_premult_alpha. I haven't tested but I think the latest update before merge broke the feature.

So we have two options:

  • Fix this one up to be blend_premult_alpha, and accept that 3D shaders will use a different keyword from 2D shaders
  • Go back to the previous version of your PR before my review (I didn't know you just replicated the inconsistency of existing 2D keywords) and use blend_premul_alpha in 3D too.

And yes to be clear we shouldn't change 2D shaders, that would break compat.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't remember either, and frankly I wonder if I did it on purpose or not. I'm not sure if we want to keep consistent with 2d and generally inconsistent, or if we should have correct 3d and weird 2D. Is there any precedent we can reference to take the decision?

@@ -609,6 +609,9 @@
<constant name="BLEND_MODE_MUL" value="3" enum="BlendMode">
The color of the object is multiplied by the background.
</constant>
<constant name="BLEND_MODE_PREMULT_ALPHA" value="4" enum="BlendMode">
The color of the object is added to the background and the alpha channel is used to mask out the background. This is effectively a hybrid of the blend mix and add modes, useful for FX like fire where you want the flame to add but the smoke to mix. By default, this works with unshaded materials using premultiplied textures. For shaded materials, use the PREMUL_ALPHA_FACTOR built-in so that lighting can be modulated as well.
Copy link
Member

Choose a reason for hiding this comment

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

Should be [code]PREMULT_ALPHA_FACTOR[/code].

@jitspoe
Copy link
Contributor

jitspoe commented May 1, 2024

Does this PR address fog making stuff light up that should be transparent? That was one thing I never got around to fixing on mine.

case BLEND_MODE_PREMULT_ALPHA: {
// This is unlikely to ever be used for detail textures, and in order for it to function in the editor, another bit must be used in MaterialKey,
// but there are only 5 bits left, so I'm going to leave this disabled unless it's actually requested.
//code += "\tvec3 detail = (1.0-detail_tex.a)*ALBEDO.rgb+detail_tex.rgb;\n";
Copy link
Contributor

Choose a reason for hiding this comment

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

@QbieShay I don't understand why this is commented out. Before the nuance is lost to the sands of time can you expand a little more? 5 bits left where?

uint64_t detail_blend_mode : get_num_bits(BLEND_MODE_MAX - 1);

The bits as far as I can tell are already spent allocating BLEND_MODE_PREMULT_ALPHA. Commenting it out just seems a little strange.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, that was probably taken from my original PR which didn't have the bits taken. I was hesitant to waste a bit for this, but if the bit is already taken, this should be uncommented.

@jitspoe
Copy link
Contributor

jitspoe commented May 9, 2024

Ok, just cherry-picked this and tested and verified that this does NOT properly address fog, at least independently. I did notice there were some other fog related things in the shader file that changed, but I don't think they're related. Guess that should still be addressed.

To better explain, I'm using flame sprites with premult alpha, and if I have fog enabled, the whole quad starts becoming visible, (additively blended with the fog color). I thought maybe the premult_alpha_factor would somehow get used, but looking closer, I'm not really sure what premult_alpha_factor even does, as it seems to just be renamed to premult_alpha, which is a bit confusing.

@QbieShay
Copy link
Contributor Author

QbieShay commented May 9, 2024

@jitspoe it's multiplied at the very end of the shader. Do you mind supplying a test scene with your setup (via DM is fine too) so that u can try and fix the dog issue?

@clayjohn
Copy link
Member

clayjohn commented May 9, 2024

Ok, just cherry-picked this and tested and verified that this does NOT properly address fog, at least independently. I did notice there were some other fog related things in the shader file that changed, but I don't think they're related. Guess that should still be addressed.

To better explain, I'm using flame sprites with premult alpha, and if I have fog enabled, the whole quad starts becoming visible, (additively blended with the fog color). I thought maybe the premult_alpha_factor would somehow get used, but looking closer, I'm not really sure what premult_alpha_factor even does, as it seems to just be renamed to premult_alpha, which is a bit confusing.

Can you share your shader? Are you writing to PREMUL_ALPHA_FACTOR?

@akien-mga
Copy link
Member

Make sure to also cherry-pick #91399 if testing on a branch different from upstream master, as this PR wouldn't be functional without that fixup.

@QbieShay
Copy link
Contributor Author

I havent had a chance to boot my dev PC yet but I think I understand the issue, I'll debug it later. If it is what I imagine, I need to multiply fog by alpha if render mode pmul alpha is on.

@clayjohn
Copy link
Member

I havent had a chance to boot my dev PC yet but I think I understand the issue, I'll debug it later. If it is what I imagine, I need to multiply fog by alpha if render mode pmul alpha is on.

Pmul alpha is already multiplied after fog though. I don't see why you would need to multiply it twice

@QbieShay
Copy link
Contributor Author

Because the fog should be applied only to the non transparent parts of a quad. Alpha zero, factor non zero = additive.

@QbieShay
Copy link
Contributor Author

@jitspoe Please test against latest master, I can't reproduce. Don't forget to use the PREMUL_ALPHA_FACTOR

@jitspoe
Copy link
Contributor

jitspoe commented May 17, 2024

So it seems like PREMUL_ALPHA_FACTOR just scales the albedo color value, effectively turning it into a regular alpha, kind of defeating the purpose of the premultiplied aspect. Fixes the fog, but the whole point is to be able to have additive things like flames mixed in with alpha masked stuff like smoke. Unless I'm just not understanding how this is intended to be used? Still trying to understand the reasoning behind PREMUL_ALPHA_FACTOR. How is it different from just scaling ALBEDO?

Building latest main now to do further tests.

@jitspoe
Copy link
Contributor

jitspoe commented May 17, 2024

I'm moving the discussion to godotengine/godot-proposals#3431 (comment) so it's in one cohesive location, since there is a lot of other discussion in there as well.

@xa8et67
Copy link

xa8et67 commented Jun 4, 2024

太棒了,多年来终于解决了这个问题。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

10 participants