godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
88.87k stars 20.15k forks source link

Identical `spatial`/`canvas_item` fragment shaders differ in output, when not using Compatibility/GLES2 renderer #96061

Open Wierdox opened 2 weeks ago

Wierdox commented 2 weeks ago

Tested versions

v4.3.stable.official [77dcf97d8], v4.0.stable.official [92bee43ad], v3.5.2.stable.official [170ba337a]

System information

Godot v4.2.2.stable - Windows 10.0.19045 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 1660 SUPER (NVIDIA; 32.0.15.5612) - AMD Ryzen 5 5600G with Radeon Graphics (12 Threads)

Issue description

I was trying to translate canvas_item shaders to spatial and vice versa, in curiosity for how their performance would differ. Some were fine, but others just seemed to have different coloration and I was confused. Then I tested a basic linear gradient, and it seems that spatial isn't linear for some reason?


This is the case in both Forward+ and Mobile renderers, but in Compatibility it looks fine!

Godot_v4 2 2-stable_win64_R2vvGmQI6z

Forward+

Godot_v4 2 2-stable_win64_DuekR53VzL

Compatibility

Here are the identical fragment shaders for both spatial and canvas_item:

Spatial Code ```GLSL shader_type spatial; render_mode blend_mix, unshaded; void vertex() { POSITION = vec4(VERTEX, 1.0); } void fragment() { ALBEDO = vec3(1.0 - SCREEN_UV.x, 0.0, 0.0); //ALBEDO = vec3(1.0 - UV.x, 0.0, 0.0); // The same as above. } ``` *`spatial` shader uses `vertex()` to act as fullscreen, but the bug happens regardless.
CanvasItem Code ```GLSL shader_type canvas_item; render_mode blend_mix, unshaded; void fragment() { COLOR.rgb = vec3(1.0 - SCREEN_UV.x, 0.0, 0.0); //COLOR.rgb = vec3(1.0 - UV.x, 0.0, 0.0); // The same as above. } ```

Interestingly enough, in Godot 3.5 it's the same thing; GLES3 has the same issue and GLES2 doesn't, even though Godot 4 uses GLES3 for Compatibility(afaik). I have tried many combos of render_modes, but it doesn't seem to fix the problem.

This seems like a bug to me, but I wouldn't be surprised if this is intended and I just don't get it.

Steps to reproduce

Open the repro and run main.tscn, bug is only apparent when not using Compatibility/GLES2 renderer.

Minimal reproduction project (MRP)

ShaderBugRepro.zip, 4.x

ShaderBugRepro3.zip, 3.x

AThousandShips commented 2 weeks ago

I believe this is because the compatibility renderer uses sRGB but the forward and mobile renderers use HDR, this is documented but not as clearly as it should be, see here, it's mentioned here as well but not clearly

See also:

Wierdox commented 2 weeks ago

Indeed, using an sRBG -> Linear conversion function on the input value seems to have fixed my perceived problem. And as I understand it now, I can also use source_color to convert sampler2D uniforms; which I had no clue, I thought it was just for the inspector hint to use a color wheel for vec3/4.

This part of the godot docs makes the conversion importance more clear I think, Docs/ShadingLanguage/Uniforms. It also says that if hdr_2d is enabled in settings, then the 2D shaders no longer use sRGB as default anymore. And yep, enabling that causes the canvas_item shader to look like the spatial one before the 'fix'.

The function I mentioned ```GLSL float sRGB_to_linear(float value) { // Send this function a decimal sRGB gamma encoded color value // between 0.0 and 1.0, and it returns a linearized value. if (value <= 0.04045) { return value / 12.92; } else { return pow(((value + 0.055)/1.055), 2.4); } } ```

So it seems very intentional then, I just had no clue this was something to worry about. At any rate, now I have to figure out if I want to enable hdr_2d or not for consistency.