COLOR value in shader behaves weirdly in Godot 4
Asked Answered
K

8

0

I moved to Godot 4.0.2 yesterday and noticed strange behaviour with the COLOR value in the fragment() function of my shader. I have a GPUParticles2D particle system that has a basic shader applied to its material:

shader_type canvas_item;
render_mode blend_mix;

vec3 invert_color(vec3 col_in){
    return 1.0 - col_in;
}

void fragment() {
    vec4 img_col_rgba = texture(TEXTURE, UV);  // original image
    vec3 inv_img_col_rgb = invert_color(img_col_rgba.rgb); // inverted image

    vec3 tint_col_rgb = COLOR.rgb;  // tint color
    vec3 inv_tint_col_rgb = invert_color(tint_col_rgb);  // inverted tint color

    vec3 rgb = invert_color(inv_img_col_rgb * inv_tint_col_rgb);  // tint the image
    vec4 rgba = vec4(rgb, img_col_rgba.a);  // transfer old alpha value to the new image
	
    COLOR = rgba;  // apply changes
}

This shader is supposed to tint only the black part of the particle texture by inverting the texture colours, inverting the tint colour, mixing them together, and inverting the tinted texture once more. This results in the outer part of the particle being coloured, while the inside stays white.

This worked beautifully in Godot 3.5:

But in Godot 4.0.2 it seems to only tint the gray transition between white and black:

I noticed that the shader works as expected if I replace COLOR.rgb in line 12 with a static colour like vec3(1, 0, 0).
And since the exact same code works in older Godot versions as well as in other engines like Unity, I'm assuming that the COLOR changed its behaviour in Godot 4. Is this a bug or am I missing something? Is there maybe a better way to achieve this effect all together?

Kaohsiung answered 9/4, 2023 at 3:53 Comment(0)
K
0

Sorry for the very long pause - lots of final exams.

Anyways, after tinkering around some more I determined that the shader, or at least the very specific implementation that I want, is simply no longer possible in version 4. Instead I'm going to use a texture atlas of 8 frames containing pre-coloured versions of the same particle that I animate using frame interpolation.

Visually it doesn't make a big difference, but it's definitely less convenient from the programming side of things as I now have to create a new atlas for every colour transition instead of switching colour ramps. But better than nothing, I guess.

Kaohsiung answered 5/6, 2023 at 11:44 Comment(0)
C
0

What happens if you try

vec3 tint_col_rgb = textureLod(TEXTURE, UV, 0.0).rgb;

Chaussure answered 9/4, 2023 at 4:17 Comment(0)
B
0

COLOR is an output AFAIK. What do you expect to be in there?

Bradfield answered 9/4, 2023 at 6:46 Comment(0)
B
0

Wait, COLOR is an inout but you set it as the vertex color in the vertex shader. Otherwise it samples from TEXTURE so you are tinting the texture with itself.

https://docs.godotengine.org/en/stable/tutorials/shaders/shader_reference/canvas_item_shader.html

Bradfield answered 9/4, 2023 at 6:48 Comment(0)
K
0

Yeah I'm new to shader programming and it shows. Anyways...
If i try vec3 tint_col_rgb = textureLod(TEXTURE, UV, 0.0).rgb; there is no colour change, just the plain texture.

I kinda expected COLOR.rgb to contain the tint colour coming from ParticleProcessMaterial, which it obviously doesn't. The error occurs when inverting the texture. I tested this using red colour coming from ParticleProcessMaterial and another red colour from a seperate uniform variable
uniform vec4 _tint_color : source_color = vec4(1, 0, 0, 1);.

In Godot 3, multiplying the negative texture with either COLOR.rgb or _tint_color results in the same behaviour - white becomes red
In Godot 4, this only works with _tint_color

As soon as I multiply the negative texture with COLOR.rgb in Godot 4, the colours become messed up

I could technically tint the particles using the uniform exclusively, but then all particles will have the same colour.
The data in COLOR.rgb must have changed somehow but it's hard to tell what exactly changed...

Kaohsiung answered 9/4, 2023 at 13:37 Comment(0)
C
0

https://docs.godotengine.org/en/stable/tutorials/shaders/shader_reference/particle_shader.html

inout vec4 COLOR - Particle color, can be written to and accessed in mesh's vertex function.

Chaussure answered 9/4, 2023 at 13:54 Comment(0)
K
0

Thanks everyone for your reply!

Particle shaders do not provide the TEXTURE variable from what I've seen/tried. Maybe pass the texture via script to the shader? Unfortunately, I couldn't look much more into it as my 2D scene broke with a Vulkan pipeline error after restarting Godot and now it refuses to load.

I ran out of time for now, but I will definitely try again in a few days though.

Kaohsiung answered 9/4, 2023 at 15:7 Comment(0)
D
0

In Godot 4, vertex colors in both 2D and 3D are unsigned 8-bit integer for performance reasons. In Godot 3.x, they were signed floating-point, so they could be greater than 1.0 (overbright color) or negative. See https://github.com/godotengine/godot/issues/75083.

Durden answered 19/4, 2023 at 1:18 Comment(0)
K
0

Sorry for the very long pause - lots of final exams.

Anyways, after tinkering around some more I determined that the shader, or at least the very specific implementation that I want, is simply no longer possible in version 4. Instead I'm going to use a texture atlas of 8 frames containing pre-coloured versions of the same particle that I animate using frame interpolation.

Visually it doesn't make a big difference, but it's definitely less convenient from the programming side of things as I now have to create a new atlas for every colour transition instead of switching colour ramps. But better than nothing, I guess.

Kaohsiung answered 5/6, 2023 at 11:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.