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

2D-HDR may cause issues with texture alpha rendering #80868

Open
Novark opened this issue Aug 21, 2023 · 7 comments
Open

2D-HDR may cause issues with texture alpha rendering #80868

Novark opened this issue Aug 21, 2023 · 7 comments

Comments

@Novark
Copy link

Novark commented Aug 21, 2023

Godot version

v4.2.dev3.official

System information

Windows 11 [Vulkan (Forward+)]

Issue description

Enabling 2D-HDR support seems to mess up the alpha channel of imported textures (used in a Sprite2D in this example). I don't know if this is an intended side-effect that needs to be worked around somehow, or if this is a bug. See the image below for an example.

image

Texture import settings are defaulted, as follows:

image

Steps to reproduce

Here's the sample border image that contains alpha information. Place it on a black (or very dark) background and enable/disable the 2D-HDR option and observe how the alpha transparency changes.

BFSquare_Border

Minimal reproduction project

All information to reproduce is included above.

Also tested with the #80651 PR changes and the issue still persists.

@clayjohn
Copy link
Member

clayjohn commented Aug 22, 2023

This is a consequence of doing blending in linear space instead of sRGB space. When using HDR, blend operations will not result in the exact same colors as when not using HDR. Its an unavoidable consequence of using HDR.

Compare the HDR version to the same image in the 3D renderer and you will see that the colors look the same.
image

This fact should be better documented

@Novark
Copy link
Author

Novark commented Aug 22, 2023

This is a consequence of doing blending in linear space instead of sRGB space. When using HDR, blend operations will not result in the exact same colors as when not using HDR. Its an unavoidable consequence of using HDR.

Compare the HDR version to the same image in the 3D renderer and you will see that the colors look the same.
[Image]

This fact should be better documented

Thanks for that clarification. Do you know if there are any transformations or "standard workarounds" that are typically recommended in this case, or any further reading material that you'd suggest?

Right now I'm probably looking at editing the base images to look proper with HDR, so I'd like to rule out other potential fixes before I proceed with these edits.

It almost begs for the ability to have a checkbox for texture imports that says something like "Render this texture in sRGB space instead of linear space". I think most folks who enable HDR (at the moment anyway) are doing so to get the 3.x glow back into their projects, and don't actually want their sprites to be rendered in a different colour space. I'm not sure how feasible this is, however.

Edit: After some cursory reading, it seems that it's suggested to do any blending in sRGB space before rendering in linear space, however, I'm not sure if this is currently possible in Godot. I've also seen some discussion that suggests that alpha channel values should be treated in sRGB space and not converted to linear space, since the channel does not contain colour data, but rather a 0.0 --> 1.0 value to denote transparency of the respective RGB colour channels. I can't tell from my initial testing above if the "HDR enabled" version of the image is a result of the RGB linear-space conversion (and thus, brighter), or if it's also shifting the alpha channel as well, resulting in a "less transparent" border image. Can you confirm if the renderer also converts the alpha channel to linear space @clayjohn ?

@Novark
Copy link
Author

Novark commented Mar 8, 2024

@clayjohn Apologies for the ping - I was hoping you might be able to provide some clarity on my previous edit regarding the conversion of channel alpha from gamma to linear space.

Some related discussion can be found here. In particular the reply stating the following:

Doesn't make sense for the alpha channel to be gamma corrected, since it's not color data that you will be displaying directly on screen. It's a "percentage" value that indicates transparency. Applying gamma correction to it would break blending (transparency would not be linear anymore).

Imho, alpha should be stored in linear space directly.

I'm curious what your thoughts are on this, and how it's currently done in Godot's forward renderer.

I'm also still a bit unclear about how blending is calculated before and after the HDR changes. The impression I get from reading some literature is that blending should somehow receive special treatment during colour-space conversions (by virtue of things like pre-multiplied alpha, compression, etc...). I'm not sure if this distinction even makes sense in the context of Godot's HDR changes. Does enabling HDR simply convert the entire screen's RGBA from gamma to linear colour space before dumping everything to the framebuffer, or is it more complicated than that?

My knowledge of Godot's renderer is admittedly quite limited, and I'm also not used to working in two colour spaces at the same time, so I'm unsure if I'm thinking about this in the correct way. The only place I've run into issues so far with HDR is with blending, both in blending (overlapping) textures with alpha channel data, and in some of my shaders where I'm applying different blend modes to vec4 colours (some similar discussion / potential issues in #84989 can be found here).

@clayjohn
Copy link
Member

I won't have a chance to write up a full explanation anytime soon. But it sounds like you would benefit from a deeper explanation of how blending works and why color space impacts blending.

Just to quickly address the main points. When doing color space conversions, we always leave the alpha channel as is. Alpha is in neither linear nor gamma space since it is a part of the color. So that never changes.

What changes is the appearance of the final blended color after blending two colors together. And the reason for that is simple math. Alpha blending is essentially just a way of adding two colors together, to simplify consider the following equation:

A + B = C

Let's say that these represent colors in the linear color space. If A is 0.5 and B is 0.1, then C is 0.6. That's really easy to calculate. But now, we want to store A and B in a different "color space" we can create a color space with any equation, I'm going to use power of two. We can call this color space po2-space.
Here is how things change:

A = 0.5
A2 = 0.5 * 0.5 = 0.25
B = 0.1
B2 = 0.1 * 0.1 = 0.01
A2 + B2 = C2
C2 = 0.26
C = sqrt(C2) = 0.5099

Notice how the blending equation is the exact same (just a simple addition), but the final result is different. The same thing happens with gamma vs. linear. Blending itself produces different values when you use a different color space. This can be very desirable for certain things, and at other times (like here) it can be a pain in the butt.

In Godot, you have two pathways when working with 2D:

  1. Everything is in SRGB-space. This is the most intuitive, for 2D blending operations, but doesn't work well with HDR content
  2. Everything is in Linear space and is converted to SRGB when displaying things on screen.

Also, this has nothing to do with the 3D renderer, the Mobile and Forward+ 3D renderers have always used linear space.

In either case, Godot will ensure that most of your inputs to the shader are in the correct color space (i.e. textures and color uniforms are updated automatically). If you specify a color directly in the shader code (i.e. using vec4(), then you are responsible to ensure that color is in the proper color space)

To conclude, this isn't an issue with Godot, this is a general math thing that is useful to understand if you are working with color spaces.

@Novark
Copy link
Author

Novark commented Mar 12, 2024

@clayjohn Thanks for that explanation - it was very helpful! I'll likely do some further reading on the specifics of the two colour spaces for my own edification.

A more in-depth technical write-up for the Godot docs as you mentioned would probably be helpful for users that are working with HDR, as I'm sure there are others like myself who aren't used to working in two colour spaces within a game engine. If/when such a document were to be created - I would find the following sections helpful (specifically addressing 2D projects):

  • When to use HDR, and when not to (aside from "I want 2D glow, and therefore I have to enable HDR")
  • What technical considerations went into migrating from the 3.X "fake" glow, to a proper HDR implementation? Could the same thing be achieved with a shader, and if so, what precipitated the need for a full rendering update to allow for an HDR version of 2D glow?
  • Which colour-space should users work in when managing art assets for various project types (HDR enabled/disabled)
  • Working with shaders in HDR: how to treat vec4 colours in SRGB vs. Linear space
  • Impacts on colour blending when using HDR, including examples using sprites as well as shaders

Some of this was addressed in #80215 but may benefit from being expanded on in a full godot doc writeup (when time permits).

Thanks again for the interim explanation!

@ahalldenabberton
Copy link

I ran into the same problem and posted a temporary solution on a different thread for how to make your shader handle simple color conversions manually while we wait for an update / bug fix, hope that helps.

@conde2
Copy link

conde2 commented Jul 15, 2024

I found that HDR changes canvas items that are equal or below HDR treshold.

Left image with HDR, right image without HDR
image

image

Note: I have no world evnironment in my project, and still it display different.

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

No branches or pull requests

5 participants