ihhub / fheroes2

fheroes2 is a recreation of Heroes of Might and Magic II game engine.
https://ihhub.github.io/fheroes2/
GNU General Public License v2.0
2.66k stars 371 forks source link

Screen Filter (e.g. xBRZ) #4946

Open Laserlicht opened 2 years ago

Laserlicht commented 2 years ago

Preliminary checks

Describe the problem requiring a solution

When playing on a high-resolution monitor in original resolution, everything is very small.

If you play it in a lower resolution, then it can get very blurry.

Describe the possible solution

In the HoMM3 HD mod it is possible to choose a lower resolution and increase it with a filter. xBRZ x4 +Hermite (at 1870x1052 on a 1440p monitor) for example gives a very good, sharp result and is a good compromise. Of course, it costs computing power, that's clear.

Unfortunately I don't have enough experience to implement something like this myself. But perhaps others will also find the idea good.

Additional info

No response

ihhub commented 2 years ago

Hi @Laserlicht , what do you mean by blurry graphics? The game has nothing related to filtering. Did you try 800x600 or 1024x768 resolutions? Do you play in fullscreen? If not why don't you try to resize the window?

Also please specify your OS and what exact resolution and settings you have for your monitor.

Laserlicht commented 2 years ago

Hi @ihhub

Sorry, I was not precise enough.

Yes, full screen experience. But with a lower resolution than that the monitor. So that the gui elements are larger (and therefore also blurrier).

Laserlicht commented 2 years ago

Example: If I use 1600x900 on my 2560x1440 monitor it would look like this (view it in 100%): image

If I apply an xBrz filter on it (e.g. this) then it would look like this (view it in 100%): xbrz

In my opinion much better.

afrikansviridovic commented 2 years ago

@Laserlicht Have you tried 1280x720 with integer scaling option (AFAIK it is available for AMD and nVidia graphic cards)?

Laserlicht commented 2 years ago

@afrikansviridovic: The difference is very small.

We could e.g. use the xbrz library from Zenju.

For me, however, this is too much. Using the filter is not that hard, but you have to understand the rendering of fheroes2 first. In principle, we would only have to exchange the image before transferring it to sdl.

Furthermore we should have a parameter to change the scalefactor / deactivate.

a1exsh commented 2 years ago

@Laserlicht interesting. If you still have the save file to produce exactly the same screenshot, could you try with the following hint changed to "nearest" instead of "linear"? https://github.com/ihhub/fheroes2/blob/bbb68b95e330003a09fa589cb4b4c35b3537994e/src/engine/screen.cpp#L946

See here for possible values: https://wiki.libsdl.org/SDL_HINT_RENDER_SCALE_QUALITY

Laserlicht commented 2 years ago

Yes, another example (this time at 1360x768 on 2560x1440 - in my opinion a good choice for balance between gui size and hi-res):

View it in 100%

Original: original

Linear: linear

Nearest: nearest

XBR x3 (+ linear downscale): xbr x3

ihhub commented 2 years ago

I have a feeling that we're trying to fix an OS issue. If the application generates 800x600 image / frame in full screen mode then the monitor will display the same image. However, the modern OSes have an option of software scaling by using some blurring based algorithms to upscale the image for higher resolution. What the proposed filters do in theory is to do deconvolution which never gives precise results without introducing artifacts.

@Laserlicht if you're using MacOS or Windows please disable OS scaling to verify my explanation.

Laserlicht commented 2 years ago

I have done the tests with Ubuntu. In Ubuntu, the operating system or SDL seems to do the scaling. But this is basically comparable in (scaled) window mode.

I do not see this as an OS issue.

After all, the buttons in the GUI are fixed in size. This means that if you have a high pixel density, you have no choice but to render it in a smaller resolution and scale it up. The only question is how this should be done.

Emulators usually have a large selection of filter options. Of course, the focus is much stronger there, because the difference between the original resolution and the screen resolution is immense.

But of course a filter can only provide a nicer picture, but not more information.

The solution of Heroes 3 HD mod, however, I find very good.

With @a1exsh tip you can at least switch to nearest, which is often better than linear. But in my opinion not up to XBRZ or similar.

ihhub commented 2 years ago

@Laserlicht , have you tried 1024x768 resolution in full screen mode? Exactly this resolution, not the higher one. How does it look? Isn't it what you're looking for?

Laserlicht commented 2 years ago

image It is blurry too, because 768 is not an even divisor of 1440.

CommonLoon102 commented 2 years ago

This game obviously should use nearest scaling instead of linear because linear makes it blurry. This game contains pixel art and it is intended to show as it is without any filtering. There SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "linear" ); it should be changed to "nearest" and problem is solved. Or at least make it configurable. (I play it in 640x480 and if I resize the window to be larger, it makes everything blurry)

a1exsh commented 2 years ago

"nearest" is not without issues — I've tried that and could immediately notice that the small font becomes ugly (2px lines mixed with 1px).

Laserlicht commented 2 years ago

This game obviously should use nearest scaling instead of linear because linear makes it blurry. This game contains pixel art and it is intended to show as it is without any filtering. There SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "linear" ); it should be changed to "nearest" and problem is solved. Or at least make it configurable. (I play it in 640x480 and if I resize the window to be larger, it makes everything blurry)

Nearest filter is a personal preference. Almost everyone has a different opinion.

I prefer xBRZ or hqx, even if the pixel feeling is lost.

That's why a setting option would be good. Comparable to HoMM3 HD.

HoMM3 HD: image image

Effektus commented 2 years ago

@Laserlicht interesting. If you still have the save file to produce exactly the same screenshot, could you try with the following hint changed to "nearest" instead of "linear"?

https://github.com/ihhub/fheroes2/blob/bbb68b95e330003a09fa589cb4b4c35b3537994e/src/engine/screen.cpp#L946

See here for possible values: https://wiki.libsdl.org/SDL_HINT_RENDER_SCALE_QUALITY

It was be great to have options in menu for this problem.

ihhub commented 2 years ago

As I mentioned in this comment it is good to know what problem are we going to solve. Is it to have a bigger picture preserving the same image quality? Or adding some special effects on the end result frame?

We already have in-game Resize() function which does subpixel scaling with maximum information preservation but the biggest issue is that the game is based on 8-bit images with a limited palette, not on RGBA images like for Heroes 3.

Also all these wrappers are weak solution to solve existing problems just because there are no other ways to modify the original game's image rendering. I recommend to think beyond "let's modify image X1xY1 into image X2xY2 by applying a scaling function Z".

Effektus commented 2 years ago

With diferent options in menu must be solves all tasks. preserving the same image quality and adding some special effects. Everyone will decide for themselves how they want to play if they have a choice.

ihhub commented 2 years ago

This feature is the lowest priority for now and could be implemented only after 1.0 as the project still has many unsolved issues for 1.0 release.

Effektus commented 2 years ago

Оf course :)

chrisbigart commented 2 years ago

I made a quick and dirty proof of concept of what this would look like using xBRZ scaling (and high-DPI awareness). Current scaling: normal_res2 xBrz: xbrz2 xbrz3

This is simply doing what you said above, "let's modify image X1xY1 into image X2xY2 by applying a scaling function Z", but I (personally) think the results look pretty good and would be a nice quality of life improvement.

Implementation wise, this modifies the surface that the final frame is rendered to by a constant scaling factor int scaleFactor = 2; ... _surface = SDL_CreateRGBSurface( 0, width_ * scaleFactor, height_ * scaleFactor, isPaletteModeSupported ? 8 : 32, 0, 0, 0, 0 ); The logical size ('SDL_RenderSetLogicalSize') remains the same so that internally, coordinates aren't affected.

Then, when the scene is rendered, the palette transform is applied and the results are stored in a temporary surface of the original, unscaled width and height. xBRZ (or whatever scaling algorithm) is then applied with the scaled surface as the output:

static SDL_Surface * out_surface = SDL_CreateRGBSurface( 0, imageWidth, imageHeight, 32, 0, 0, 0, 0 ); ... xbrz::scale( scale, ( (uint32_t *)out ) + imageWidth * 2, (uint32_t*)surface->pixels, imageWidth, imageHeight - 4, xbrz::ColorFormat::RGB );

This has some issues, notably that it is really slow.

chrisbigart commented 2 years ago

Thinking about it, it might make more sense to render each asset (sprite, tile, etc) upscaled, then simply blit the upscaled versions onto the final surface. This should be much faster, and be slightly higher quality, at the expense of some run-time memory usage.

CommonLoon102 commented 2 years ago

Also, please have an option for nearest.

ihhub commented 1 year ago

Hi @Laserlicht , the latest version of fheroes2 contains a plenty of resolution options including those with integer scaling. Does it solve your original issue?

Laserlicht commented 1 year ago

Hi @ihhub

No not really because:

But:

But I think this can postponed until SDL3 releases. Which should support shaders. xBrz for example can runned as shader: https://github.com/libretro/glsl-shaders/tree/master/xbrz

vermian commented 1 year ago

Can we get a sharp scale option instead of only bilinear filtering? Essentially looking for pixel doubling when you pick 2.0x scale like 960x540 so the pixels look sharp on a 1080p display. Currently the image is quite blurry unless I want to pick a native 1920x1080 image which makes everything too small.

a1exsh commented 1 year ago

Can we get a sharp scale option instead of only bilinear filtering?

There was a setting to choose between "linear" and "nearest (neighbor)" scaling. The code is still there in the engine, but I don't know if the setting from the cfg file is respected.

vermian commented 1 year ago

Can we get a sharp scale option instead of only bilinear filtering?

There was a setting to choose between "linear" and "nearest (neighbor)" scaling. The code is still there in the engine, but I don't know if the setting from the cfg file is respected.

Oh, you're right, and it does work. I set "screen scaling type = nearest" - was "linear" before. Now it has sharp pixels. Would be nice if this was an option in the UI but this works, thanks.