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

Godot stretch mode with keep aspect making every pixel oddly shaped #45262

Closed
chucklepie opened this issue Jan 17, 2021 · 7 comments
Closed

Godot stretch mode with keep aspect making every pixel oddly shaped #45262

chucklepie opened this issue Jan 17, 2021 · 7 comments

Comments

@chucklepie
Copy link

chucklepie commented Jan 17, 2021

Godot version:
3.2.3

OS/device including version:
linux mint

Issue description:
Cannot get bilinear filtering to work on stretching a screen.

I have been through every setting in godot and cannot find an option to enable something like bilinear filtering on the screen. All I could find was a per image filter. But either way I have tried I have tried pixel snap, every stretch mode, every aspect mode, all the batching/quality settings on and off and every permutation.

I would have thought stretching a screen would be applying some kind of algorithm like bilinear on the whole screen and not on a per image basis via 'filter' but could find no option for this.

But regardless, if the 'filter' option on image import is how you do it all I only get is what looks like nearest neighbour.

Observe the image below of the yellow/blue sprite.
image

As you can see every other line is stretch horizontally or vertically the pixels. They are all meant to be exact squares of the same size. Enabling/disabling 'filter' on the image looks ok inside Godot, but as soon as I run my program, all filters are removed. I have cleared my .import folder out and it looks filtered in the editor, just not runtime.

image

@chucklepie chucklepie changed the title Godot not filtering graphics at runtime Godot stretch mode with keep aspect making every pixel oddly shaped Jan 17, 2021
@Calinou
Copy link
Member

Calinou commented Jan 17, 2021

Cannot get bilinear filtering to work on stretching a screen.

I have been through every setting in godot and cannot find an option to enable something like bilinear filtering on the screen. All I could find was a per image filter. But either way I have tried I have tried pixel snap, every stretch mode, every aspect mode, all the batching/quality settings on and off and every permutation.

There is no setting for this out of the box. You need to use the TextureRect + ViewportTexture approach to get bilinear filtering in a viewport. See 3D viewport scaling demo for an example of this – the same approach also works in 2D.

That said, in master, the viewport stretch mode uses bilinear filtering by default. It's unknown whether this is intentional or an oversight, but an option to toggle between nearest-neighbor filtering and bilinear filtering should be provided in the project settings.

Your viewport should never appear stretched when using the keep stretch aspect, so the issue must be coming from somewhere else (double-check your Node2D/Control scale properties).

Closing, since this is not a bug.

@chucklepie
Copy link
Author

chucklepie commented Jan 17, 2021

This is definitely not bilinear filtering I can tell you that much, it is nearest neighbour :)

So Godot cannot stretch an viewport and provide a way to avoid every pixel being non-linearly stretched even with stretch mode kept to aspect and filtering enabled?

image

@Calinou
Copy link
Member

Calinou commented Jan 17, 2021

So Godot cannot stretch an viewport and provide a way to avoid every pixel being non-linearly stretched even with stretch mode kept to aspect and filtering enabled?

Not out of the box, but you can do it using a script. See godotengine/godot-proposals#1666.

@blaise-rascal
Copy link

So Godot cannot stretch an viewport and provide a way to avoid every pixel being non-linearly stretched even with stretch mode kept to aspect and filtering enabled?

Not out of the box, but you can do it using a script. See godotengine/godot-proposals#1666.

We want to implement bilinear filtered scaling. As I read it, this proposal you link aims to implement largest-integer scaling, so it's not really addressing the issue.

As chucklepie notes, the 3d viewport scaling demo is also not of use, because the only scaling algorithm available to viewports appears to be nearest-neighbor. It's not even nearest-neighbor based on the size of the final (big) image, but rather, the size of the initial (small) image.

I've tried all kinds of things - nested viewports, shaders, etc. to solve this problem. So far, the best method I've found, is to export all my sprites at 500% scale in aseprite. (Other percentages like 400% may also work.) So a 320x180 background would be 1600x900. I import all my sprites with filtering enabled. Then under project settings, I make the window size be 1600x900, the test size be something like 960x540, and turn stretch mode to 2d, keep aspect. (And I turn off pixel snap.) It sucks for the game's textures to be 5x the scale (and thus 25x the filesize) they need to be, but I can't come up with anything better at the moment. (Tip: Turn on grid snap with the grid step being 5x5 pixels for easier scene composition. Also you may need to give a 0.5px offset to some sprites & cameras to get everything lined up well.)

Here's an example project made with this method. It can scale up gracefully to go fullscreen in any monitor size: https://blaise-rascal.itch.io/500-sprite-scaling-demo

@Calinou
Copy link
Member

Calinou commented Jan 26, 2021

As chucklepie notes, the 3d viewport scaling demo is also not of use, because the only scaling algorithm available to viewports appears to be nearest-neighbor. It's not even nearest-neighbor based on the size of the final (big) image, but rather, the size of the initial (small) image.

You can get bilinear filtering by enabling Filter on the ViewportTexture. The 3D viewport scaling demo provides an option for that.

If you want to have non-integer pixel scaling that looks decent (not as crisp as nearest-neighbor integer scaling, but still has a "pixel" feeling to it), you can render non-filtered sprites to a supersampled viewport then display it in a ViewportTexture with filtering enabled. This is what macOS does to emulate fractional scaling.
For example, if you choose 1.5× scaling, macOS will internally render at 3× then will downscale everything by a 50% factor (resulting in 1.5× scaling). You should downscale by an integer factor (50%, 33.3%, 25%, …) to get the crispest possible result.

@blaise-rascal
Copy link

If you want to have non-integer pixel scaling that looks decent (not as crisp as nearest-neighbor integer scaling, but still has a "pixel" feeling to it), you can render non-filtered sprites to a supersampled viewport then display it in a ViewportTexture with filtering enabled. This is what macOS does to emulate fractional scaling.
For example, if you choose 1.5× scaling, macOS will internally render at 3× then will downscale everything by a 50% factor (resulting in 1.5× scaling). You should downscale by an integer factor (50%, 33.3%, 25%, …) to get the crispest possible result.

Thanks for the reply! I've tried to implement what you are suggesting, but I've never gotten it to work. (And neither has anyone I've encountered on reddit, youtube, etc.)

Here is an attempt to modify the 3d viewport scaling demo to show off a 2d pixel game scene: https://blaise-rascal.itch.io/2d-scaling-attempt

Source code here: https://github.com/blaise-rascal/3d_scaling_sandbox_2

As you can see, the results are less than ideal at 25% scaling. First of all, it does nearest-neighbor scaling according to the smaller pixel grid (as I had previously stated), which leads to an extremely jerky camera when you move around with the arrow keys. Second, the "filter" option doesn't seem to affect the scaling; it just makes everything blurry, and it's applied before scaling, making the end results VERY blurry.

I just don't think what you're saying - "supersampled viewport" - in the way that I'm imagining it, is possible. Again, see my 500-sprite-scaling-demo project linked in my previous post, to see what I'd love to be able to do with a 320x180 pixel game scene.

@Calinou
Copy link
Member

Calinou commented Jan 27, 2021

I'm pretty sure what I said is already possible in Godot, but I don't have time to try to replicate it. Also, remember that it will never be as crisp as integer nearest-neighbor scaling. You have to accept that it'll always look a little blurry at the end.

I just don't think what you're saying - "supersampled viewport" - in the way that I'm imagining it, is possible

The 3D scaling demo uses supersampling if you set the scaling factor above 100%. (Filter is automatically enabled behind the scenes in this case, as there's no point in using a scaling factor above 100% with filtering disabled.)

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

No branches or pull requests

3 participants