Mutiple resolution handling for HD 2d game

Godot Version

4.3

Question

I am making a top down 2d game with 4k graphics and want to support a range of resolutions.

Question 1
The goal is to have “field of view” scaling such that game assets appear physically around the same size on different devices and larger screens show more of the game world. For this, I’ve set the stretch mode to disabled and do a custom scaling based on screen DPI. I’ve also set the texture filter to linear.

For the scaling I was looking into window content_scale_factor and camera zoom as my options. Is there a difference between these? Visually they look the same to me. Is there a performance impact?

Question 2
Additionally with this setup, my assets look pixelated and deformed on lower resolution screens. Godot documentation recommends enabling mipmaps for this case but the assets still look the same for me.

What are mipmaps supposed to achieve in this case? My understanding is that they provide low resolution assets using linear scaling, which is supposed to prevent graininess. But godot also uses linear scaling by default with window scaling or camera zoom, so I get the same results.

Q1

I’ve set mine up as 1920x1080 base resolution, and in the project settings under Display->Window I’ve got mode to “viewport”, aspect as “keep”, and scale mode as “fractional”. It’s scaling the 2D assets I have reasonably well.

Q2

Mipmaps are scaled versions of your image assets. They’re power-of-two scaled; each level is half the width and height of the previous, so each successive mip layer is 1/4 the area of the next layer up. Practically speaking, this means enabling mipmaps adds about 50% to the size of the image.

Texture filtering is an important consideration here. Let’s say we’ve got a (tiny, for the sake of illustration) grayscale texture that’s 4x4 pixels:

Base Image                   Mip Level 1      Mip Level 2

+-----+-----+-----+-----+    +-----+-----+    +-----+
|     |     |     |     |    |     |     |    |     |
| 0.0 | 0.0 | 0.0 | 1.0 |    | 0.0 | 0.5 |    | 0.5 |
|     |     |     |     |    |     |     |    |     |
+-----+-----+-----+-----+    +-----+-----+    +-----+
|     |     |     |     |    |     |     |
| 0.0 | 0.0 | 0.0 | 1.0 |    | 0.5 | 1.0 |
|     |     |     |     |    |     |     |
+-----+-----+-----+-----+    +-----+-----+
|     |     |     |     |
| 0.0 | 0.0 | 1.0 | 1.0 |
|     |     |     |     |
+-----+-----+-----+-----+
|     |     |     |     |
| 1.0 | 1.0 | 1.0 | 1.0 |
|     |     |     |     |
+-----+-----+-----+-----+

If the renderer is trying to determine what color to pull out of the texture, it’s going to have some sort of 2D (UV or ST) coordinate that maps to a location on the texture, and that location may not be exactly in the center of a texel (a texture pixel).

What it does in that case depends on the min/mag filter mode. If the filter is set to:

  • Nearest: takes the color of the texel that’s closest to the position
  • Linear: takes a linearly interpolated value between the four closest texels
  • Nearest Mipmap: takes the closest pixel on the closest 2 mipmaps and blends them
  • Linear Mipmap: takes 4 pixels from each of the closest two mipmaps and linearly interpolates
  • Anisotropic: Like other modes, but treats texels as ovals; this is more useful in 3D where a texture may be viewed from a sharp angle

So, let’s say we wanted to take a texel from (0.49, 0.49) on that texture. In:

  • Nearest: this would snap to the texel at 1, 1 to give us 0.0
  • Linear: this would sample 1,1, 1,2, 2,1 and 2,2 to give us 0.25
  • Nearest Mipmap: this would sample at 1,1 on the top level, 0,0 on mip level 1, giving us 0.0
  • Linear Mipmap: This would sample as per Linear, and all of mip level 1 for a result of 0.375

You probably want to experiment, but my suspicion is you’ll get best results with Linear Mipmap.

4 Likes

You should probably use the camera’s zoom as it’s what everyone else does, a more usual approach will reveal less headaches

“Linear” scaling is different from “Linear mipmap” make sure you enable the correct filtering method and enable mipmaps on your image assets via the import settings.

3 Likes

Good to know camera zoom is more typical, I will stick with that. And thank you, I had tried enabling mipmaps on the asset and selecting the linear mipmap filter separately but not at the same time. I see the visual difference with both enabled, thanks so much!

Thanks so much for the in-depth explanation for mipmap filtering, that definitely clears up my confusion!

Regarding the scaling setup, this results in a set field of view for the game window right? I’m aiming for a set up where you can see more of the game world on larger screens, or if you resize your window. Does it seem like a reasonable approach to disable window stretching so everything is rendered at the original resolution, but use camera zoom to account for differences in screen DPI?

I think that approach would probably work.

1 Like