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

Sprites with subpixel world positions are displayed with unexpected and uneven displacements #41491

Closed
m6502 opened this issue Aug 24, 2020 · 14 comments

Comments

@m6502
Copy link

m6502 commented Aug 24, 2020

I tried finding this issue reported but I've come empty handed.

Godot version:

3.2.2 official build.

OS/device including version:

Windows, happens both with GLES2 and GLES3.

Issue description:

Creating a small test level I dragged many (parallax) map objects together without caring about perfect positioning of anything, so about all of them ended up in non integer (X, Y) positions.

When moving the camera the elements display at slightly unexpected positions, separating ones from the others even though all of them have equal parallax parameters, thus destroying the illusion of cohesion of the elements. If I erase the subpixel amounts of the coordinates everything displays as expected.

Further tests showed this behavior is not limited to parallax elements but is a more general issue that happens with regular 2D sprites too. If sprites have non integer positions the final display positions get jumpy, like if the final position is sometimes rounded up and sometimes down, depending on the relative position to the camera.

I think it's important to remark that the Pixel Snap option does nothing to prevent this behavior.

Steps to reproduce:

Draw a bunch of 2D sprites at non integer coordinates and scroll slowly to experience the issue.

Minimal reproduction project:

I'm including a small project with two cameras in split screen that slowly move in circles over a bunch of sprites. At the left the sprites have subpixel positions and experience this issue. At the right the sprites are at integer positions and all can be seem displaying as a solid block. I think it's reasonable to expect the left camera to display the static sprites always at the same positions relative to each other.

GIF 24-Aug-20 16-00-08

spritepositions.zip

@Calinou
Copy link
Member

Calinou commented Aug 24, 2020

Related to #35606.

I guess we can alleviate this by making 2D gizmos snap to integer coordinates unless the zoom level is really high.

@m6502
Copy link
Author

m6502 commented Aug 24, 2020

Further testing this, the amount of wobble seems to be one display pixel long regardless of the camera zoom level.

GIF 24-Aug-20 16-29-34

GIF 24-Aug-20 16-32-34

I really would like to help fix this but I'm at a loss right now at where should I take a peek. Could it be at the vertex shader side?

@Zireael07
Copy link
Contributor

Peeking at vertex shader will not help - this is the basic fact that the smallest a video card can display is a single pixel, it cannot display sub-pixel.

@m6502
Copy link
Author

m6502 commented Aug 24, 2020

The problem is not with display coordinates where of course you can't draw a pixel half way, but with world coordinates where you should be able to display your sprites at whatever world subpixel coordinates you want if the resolution and zoom allows for it. Just see the attached animated gifs to understand what I'm meaning, where sprites with same subpixel quantities all display accordingly. It's when the subpixel quantities vary that a sort of rounding happens and distorts the relative display positions.

@lawnjelly
Copy link
Member

lawnjelly commented Aug 25, 2020

If I erase the subpixel amounts of the coordinates everything displays as expected.

I think you answered your own issue. @Zireael07 is correct, aside from e.g. anti aliasing, you will tend to get sampling jitter in 2d unless you take steps to prevent it. This is a form of temporal aliasing.

This isn't a bug, more a feature, that you will find in all engines / APIs, unless you take steps to prevent it, such as snapping relative to the scene / background. See #35606 for more info.

If you don't understand why it happens, this is the equivalent to having a grid like e.g. a scrabble board, then get a photo and manually fill in dark areas in the grid to match the photo, and attempt to move the photo over the grid and repeat filling in dark areas. Notice that although the general appearance as shown in the filled in squares is the same, the exact pattern of filled in squares changes due to sampling.

If you want everything to look the same, you can take approaches such as:

  • move everything by 1 pixel (or multiples of this) increments each time, or
  • snap everything to the same scene coordinate system such that when a line traverses a pixel border, all the pixels will shift at the same time. (you will still get aliasing between lines if the coordinate system doesn't match the pixels).

If we go with our photo analogy, if the photo is pixellated with the grid scale that exactly matches the scrabble board, then even if the photo is at a float offset, you'll still see a pixel perfect match on screen and get a shift when a border is reached. If the pixellated photo scale is different, you'll get lines shifting across borders at different times. And if you have 2 photos that match the grid scale, but 1 is offset from the other by a float offset, you'll get jiggle as they cross boundaries at different times.

@m6502
Copy link
Author

m6502 commented Aug 25, 2020

Allow me to politely disagree with you both :-) If I zoom the camera enough and place one sprite at X = 0 and other at X = 0.5 I would of course expect to see them at different positions on screen. If I put 10 sprites at X = 0.0, X = 0.1, X = 0.2, X = 0.3, X = 0.4, X = 0.5, X = 0.6, X = 0.7, X = 0.8, X = 0.9 I would expect them to be at ten different places if the camera has enough zoom, and, even more important, also maintain their relative positions when I move the camera. This bug is about erratic calculation of the sprite positions, not about if it is possible to move things with subpixel quantities, which of course it is. The very existence of the pixel snap option testifies this is obviously possible, and is your ticket out of it if you want everything to snap to world pixel coordinates. Godot IS drawing the sprites at different positions already. The problem is that depending on the relative camera position the sprites at non integer places jump at different display positions because there is a bug.

I can also confirm this is not limited to sprites but happens with ColorRects too, and everything inheriting from 2D canvas.

I even think some of the jitter people are reporting with 2D movement in Godot may in fact be caused by this issue. It certainly was part of it for me when we started our current project, as we were experiencing both jitter AND these undesirable sprite position jumps.

This is not a "feature", it is a bug, and a nasty one for what it matters. And the best way to prove my case is that I may have found the reason why it happens, and a way to fix the problem for good.

I want to test it a bit before proposing it for commit, and maybe discuss it a bit at Discord. Pull request incoming :-)

@m6502
Copy link
Author

m6502 commented Aug 25, 2020

I think the fix is good. I'm trying different sceneries and it seems to work fine for me all of the time. Here is a small capture of the example project with a very zoomed camera showing two intersecting sprites with alpha borders and subpixel positions, getting perfect relative positioning in contrast to the last captures I posted before.

GIF 25-Aug-20 11-14-07

You just can't know if these are two sprites or one. As it should.

I also tried some examples like the project here #35606 and that issue gets fixed too.

GIF 25-Aug-20 10-29-01

Camera smoothing is not a problem anymore and parallax planes work fine.

Pull request incoming :-)

@m6502
Copy link
Author

m6502 commented Aug 25, 2020

Just as a follow up, I have this fixed both on the master 4.0 and the 3.2 branches. I'll try to create the pull request in a few hours.

@m6502
Copy link
Author

m6502 commented Aug 26, 2020

I just created two separate pull requests, one for the 4.0 branch and other for the 3.2 branch. Hope it's OK in this way.

I'm glad I was able to locate the source of the position distortion when moving the camera. Rock solid positioning of the sprites makes everything look so much better in my opinion :-)

m6502 added a commit to m6502/godot that referenced this issue Aug 26, 2020
Fixes the 2D canvas display positions jumping pseudo randomly when the are located at fractional coordinates and the camera changes location. Fixes godotengine#41491, godotengine#35606 etc. Godot 4.0 branch.
m6502 added a commit to m6502/godot that referenced this issue Aug 26, 2020
Fixes the 2D canvas display positions jumping pseudo randomly when the are located at fractional coordinates and the camera changes location. Fixes godotengine#41491, godotengine#35606 etc. Godot 3.2 branch.
@m6502
Copy link
Author

m6502 commented Aug 26, 2020

Made the pull requests to my own fork instead of the base repository, fixed now.

@akien-mga
Copy link
Member

Fixed by #43194 (4.0) and #43554 (3.2.4).

@berarma
Copy link
Contributor

berarma commented May 31, 2021

I'm using Godot 3.3.2 and I can still see this issue happening with the test project attached to the first post. I can't see the project setting Rendering->Quality->2d->Use_Transform_Snap mentioned in the PR. Isn't it supposed to be backported?

@Calinou
Copy link
Member

Calinou commented May 31, 2021

I'm using Godot 3.3.2 and I can still see this issue happening with the test project attached to the first post. I can't see the project setting Rendering->Quality->2d->Use_Transform_Snap mentioned in the PR. Isn't it supposed to be backported?

The change was reverted before 3.3 was released due to regressions. It needs to be redone before it can be considered for merging again.

@lawnjelly
Copy link
Member

@berarma I've just tidied up a simple demo to show one way how to do pixel games with snapping:
https://github.com/lawnjelly/godot-snapping-demo/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants