godotengine / godot

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

Strange behavior when using NORMAL in a 2D fragment shader #56528

Open balinck opened 2 years ago

balinck commented 2 years ago

Godot version

3.4.2 stable

System information

Windows 10, GLES3, NVIDIA GTX 1050 Ti

Issue description

Assigning to NORMAL from fragment() in a canvas_item shader doesn't seem to produce the expected results.

Here are two Sprites, with a Light2D between them: image

The sprites are identical except the one on the left has a Material with this shader:

shader_type canvas_item;
void fragment()
{
    NORMAL = vec3(0.0, 0.0, 1.0);
}

vec3(0.0, 0.0, 1.0) is AFAIK the same as the default, flat normal map assigned to sprites, so I expect the two sprites to be lit identically, but the left sprite isn't lit at all. Changing the values of NORMAL will cause it to pick up some light, but a flat NORMAL always produces this behavior for me. In fact, the exact same behavior can be produced by replacing the line above with just NORMAL;, so it seems like referencing NORMAL in a fragment shader at all causes some change to the sprite's normals. I couldn't find any documented behavior that explains this.

Steps to reproduce

The main scene of the minimal reproduction project is set up with two Sprites and a Light2D as detailed above. Observe that the sprite on the left ignores the effect from the Light2D unless the line in its shader's fragment function is commented out.

Minimal reproduction project

normal.zip

Calinou commented 2 years ago

@balinck Can you reproduce this after disabling Use Batching and Use Batching In Editor in the Project Settings?

Also, can you reproduce this after switching to the GLES2 renderer (Rendering > Quality > Driver > Driver Name in the Project Settings)?

balinck commented 2 years ago

@Calinou

@balinck Can you reproduce this after disabling Use Batching and Use Batching In Editor in the Project Settings?

Also, can you reproduce this after switching to the GLES2 renderer (Rendering > Quality > Driver > Driver Name in the Project Settings)?

Yes; the same behavior I described occurs with batching disabled and with the GLES2 renderer.

kleonc commented 2 years ago

I think it's caused by this part of the shader: https://github.com/godotengine/godot/blob/0b54b3d0531ed5112e5f641ac2cfc72770653d5f/drivers/gles3/shaders/canvas.glsl#L700-L703

Seems like by default normal_used is false and thus light height isn't taken into account. But in your example normal_used is true and light height is zero (the default value) so light_normal is something like (x, y, 0) which dotted with normal being (0, 0, 1) results in zero, and thus light ends up being multiplied by zero.

Of course making light height positive in your example makes the light visible: Rgc0ypc2Qq

Not sure if it's working as designed, or if something needs to be changed in here.

balinck commented 2 years ago

Nice catch. This issue can stop driving me crazy now.

It makes sense that 2D light would behave differently in the absence of user-defined normals, but this behavior isn't very intuitive. Especially since it happens even just reading from NORMAL.