godotengine / godot

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

Scaling artifacts when viewport is scaled up and texture filtering is disabled #79726

Open skeddles opened 1 year ago

skeddles commented 1 year ago

Godot version

v4.1.1.stable.official [bd6af8e0e]

System information

Windows 10

Issue description

I created a 2d scene that has texture nodes, and those texture nodes have their filter set to "nearest".

In Project Settings > General > Display > Window > Stretch > Scale, I have set this to 2.

when you start resizing the window, you notice this line of stretched/squashed pixels start to move:

Godot_v4 1-stable_mono_win64_2023-07-20_17-29-05

My theory is that this is happening when the window is stretched to an uneven width/height? The seam of distorted pixels seems to always be in the center of the screen.

Steps to reproduce

  1. created a 2d scene
  2. add a texture node with an image texture
  3. set filter to "nearest".
  4. In Project Settings > General > Display > Window > Stretch, set scale to 2
  5. run the project
  6. drag the sides of the window to resize it

Minimal reproduction project

test_game.zip

Calinou commented 1 year ago

I can confirm this on 4.2.dev 0f7625ab4 (Linux, GeForce RTX 4090 with NVIDIA 535.54.03). This occurs with all rendering methods, regardless of whether the 2D pixel snap project settings are enabled (either of them, or both). The issue occurs when the window size is odd on one of the dimensions (or both).

That said, I'm not sure if this can be resolved. You probably need something like integer scaling or sharp bilinear filtering to avoid this issue.

Do other engines have this issue when using odd window sizes with no scaling?

skeddles commented 1 year ago

I tried adding this line which rounds the window size to an even number, and as expected it hides the problem.

get_viewport().size = Vector2(round(get_viewport().size.x / 2) * 2, round(get_viewport().size.y / 2) * 2)

It's not too noticeable so it might not be a bad temp solution, though I'm not sure of the performance implications of running it every frame.

Calinou commented 1 year ago

It's not too noticeable so it might not be a bad temp solution, though I'm not sure of the performance implications of running it every frame.

You can create a Control node whose anchors and offsets are configured to be in Full Rect and connect its resized signal to a function, so that the function is called when the window size changes.

Viewport also has a size_changed signal, but this will likely cause a feedback loop if you change its size in that function.

That said, most methods in Godot have no-op checks if the new value is identical to the current one, and will do nothing in that case.

Calinou commented 8 months ago

I can still reproduce this on 4.3.dev 8f0c20ee8 (Linux), even after enabling Snap 2D Transforms to Pixel and Snap 2D Vertices to Pixel in the Project Settings. This occurs with all rendering methods. Therefore, https://github.com/godotengine/godot/pull/87297 didn't appear to resolve this particular issue.

Changing stretch scale policy to integer or using a different integer scale factor like 3.0 also doesn't resolve the issue. The only thing that makes the issue go away is using a stretch scale of 1.0, which makes me think there's a rounding error somewhere in the stretch scaling logic.

For reference, the issue also occurs if Centered is disabled on the Sprite2D. The MRP doesn't feature any Camera2D.

KeyboardDanni commented 7 months ago

Could this be affected by the operating system DPI scaling? Maybe we're ending up with an odd window size due to that?

Calinou commented 7 months ago

Could this be affected by the operating system DPI scaling? Maybe we're ending up with an odd window size due to that?

I'm using 100% scale on KDE X11, so it's not related to that. I've also tried using borderless mode and can still reproduce the issue (using Alt + Right mouse button to resize the window while in borderless mode).

Also, it's interesting to note the duplicated pixels are always in the center of the viewport.

image

image