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

GPUParticles2D colors are being rendered darker than expected #80025

Open
georgwacker opened this issue Jul 29, 2023 · 7 comments
Open

GPUParticles2D colors are being rendered darker than expected #80025

georgwacker opened this issue Jul 29, 2023 · 7 comments

Comments

@georgwacker
Copy link
Contributor

georgwacker commented Jul 29, 2023

Godot version

v4.2.dev.custom_build [75f9c97]

System information

Windows 11 - Godot 4.1.1 / master [75f9c97] - AMD RX6800, Vulkan Forward/Mobile

Issue description

particle_color

Particles from GPUParticles2D are being rendered darker than expected with the Vulkan backend, for both forward and mobile.

I noticed this with Godot 4.1.1 and the issue is still present on 75f9c97.

Steps to reproduce

Create GPUParticles2D with default ParticlesMaterial, change color, set scale, compare to ColorRect with same RGB value.

Minimal reproduction project

N/A

@Calinou
Copy link
Member

Calinou commented Jul 29, 2023

This is likely a sRGB-to-linear conversion issue (or vice versa). To confirm this, set the color to the result Color.linear_to_srgb() (or try Color.srgb_to_linear()).

@georgwacker
Copy link
Contributor Author

georgwacker commented Jul 29, 2023

I've set the color via:

process_material.set("color", Color("cb70ff").linear_to_srgb())

and it is now showing the correct color.

Color.srgb_to_linear() on the other hand is showing an even darker color.

In order of the scene list:
colors

@clayjohn
Copy link
Member

clayjohn commented Jul 31, 2023

This comes from an interesting limitation in the Particles process shaders.

When passing uniform colors to shaders, we have some logic to automatically convert the color to linear if it is being used in a shader where colors should be in linear space (spatial shader, sky shader, etc.) However, Particle process shaders can be either linear space (for 3D shaders) or sRGB space (for 2D shaders). During the actual particles process shader, there is no way to tell if you are going to use the particles in 2D or in 3D.

One option would be to always do the processing in sRGB space, then force 3D users to set the "vertex_is_srgb" flag in their material. But that would break compatibility, so isn't really a good option.

We should probably investigate giving the material some awareness of where it is used. But even that is tricky as process material uniforms are updated independently from the particles. We might be able to figure something out using a shader variation though. As the process update step has knowledge of the particles node and whether it is 2D or 3D.

I guess what could work is the following:

  1. Create a shader variant for 2D and 3D (same as what we already do for the particles copy shader)
  2. Add a wrapper define like ENSURE_COLOR_SPACE() that does a linear to sRGB conversion when 2D is defined, otherwise it does nothing
  3. In the shader compiler code, we detect if the user is accessing a variable with "source_color" and is in a particles shader. If so, we wrap the variable with ENSURE_COLOR_SPACE(). We do something similar for uv coordinates when accessing screen textures and using Multiview

@clayjohn
Copy link
Member

clayjohn commented Aug 3, 2023

I had some more ideas while working on #80215

I think I have a solution in mind that should be ideal:

  1. Cache an sRGB version of uniform set (as we do for 2D materials in Add option to enable HDR rendering in 2D #80215)
  2. At run time, choose the sRGB version if using a 2D shader, choose the linear version if 3D (this means the particles shader will be in sRGB space for 2D and linear space for 3D
  3. When drawing the particles, convert back to linear if using a linear viewport (feature added in Add option to enable HDR rendering in 2D #80215)

This should make everything "just work" and won't be a lot of work to implement. But it relies on #80215 being merged first

@QbieShay
Copy link
Contributor

QbieShay commented Sep 9, 2023

@clayjohn could be also something that we can tie to disable_z flag, since that's mostly used for 2D (a bit hacky tho)

@vybr
Copy link

vybr commented Feb 21, 2024

Any update on this @clayjohn? Not well-versed on this stuff but enabling 2D HDR also makes the particle colour ramps display colours incorrectly, but oddly the particle colour is displayed correctly only when it is enabled.

Fire Colour:
image

Smoke Colour:
image

HDR off (fire particles are correct, smoke is darker than it should be):
image

HDR on (fire incorrect, smoke is correct colour):
image

@Novark
Copy link

Novark commented Mar 8, 2024

@vybr Out of curiosity, do any of your colour ramps contain alpha channel values below 1.0 (i.e., particle transparency)? Your second colour ramp for smoke looks like it might have a white center, with a bit of a feathered (lower alpha?) near the edges, but I can't tell from your image if this is a decrease in alpha, or just a darker grey RGB colour.

My understanding is that the HDR changes will result in normal RGB values appearing slightly different, as they are converted from sRGB gamma-corrected colour space to linear RGB space. I'm still trying to determine if alpha channel values receives this same conversion treatment with Godot's forward renderer, and if so - whether that's the correct way to handle alpha channel colour space conversion. Check #80868 for some related discussion.

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

6 participants