How to target widescreen 1080p displays on multiple platforms

Ok so I’ve decided to make a 2d game for ‘big screens’. My device targets are:

Windows
Mac (maybe Linux too at some point)
AppleTV
possibly HTML5 browser at some point
(Not so interested in tablets at the moment due to the user interface)

I ideally wanted to target 1080p across the board, as if this would be a nice clean holy-grail standard i.e. 1920x1080. But looking at Unity hardware stats (http://hwstats.unity3d.com/pc/index.html) I see a much less favorable situation.

AppleTV: its for HD televisions, so that means either 1080p or 720p or even 4k for some people. I couldn’t find stats on the market share of each other than that it sounds like only the cheapest TV’s being sold these days are 720p and most are 1080p with a small % of 4k users. I’ve no idea the percentage but I know AppleTV supports 1080p and 720p and probably… 80% are 1080p? At least they’re all 16:10 aspect ratio.

Windows: 88% of users have a widescreen monitor at mostly 16:10 aspect ratio. However, only 52% of users have 1920x1080 resolution, the other 38% are lower. There are barely any 16:9 aspect displays.

Mac: 95% of users have widescreen but only a meagre 15% have 1080p. There are two other lower resolutions with over 30% market share each (1280x800 and 1440x900, both of which are 16:9). Also there’s a few % of users with 1920x1200.

Linux I have no idea but I imagine its similar to Windows since it runs on PC hardware?

HTML5: Being browser-based, although there is the option of going fullscreen (does HTML5 support that?). Either way, this means opening up to ALL resolutions. The browser display could be forced to 16:10 only with some extra bars when needed, but it still means multiple resolutions.

For 2D, maintaining ‘pixel perfect’ is going to be a challenge. It looks like it would be insane to not support 16:9 (for majority of Mac) and also other lower-res 16:10 modes on Windows, otherwise we’re looking at only 52% windows and 15% Mac market share. That means the game pretty much HAS to support resolutions lower than 1080p. Also since Mac is a bit hell-bent on 16:9, including 1920x1200, that means decisions have to be made about how to fit the display into it. If I design for 1080p (and I have a hud that runs across the bottom of the screen with a fixed width), it probably means adding black strips at the top and bottom of the display in 16:9. Not a huge deal I guess, and better than cropping horizontally.

But supporting these multiple resolutions now means the graphics have to scale down elegantly. And to do this for 2D graphics is a challenge.

The ideal would be to make unique graphics files for every resolution. i.e. ‘native graphics’. This would mean at least several sets of graphics - 1920x1080, 1920x1200, 2560x1440, 1440x900, 1280x800,1280x720, etc. That’s a lot of graphics and probably out of the question.

So likely it means one set of graphics, targeting a 1920 display width. So what’s the option… scale down the graphics. Either nearest neighbor or filtered. Filtered basically blurs the graphics and loses the ‘crisp’ clear look of exact 1:1 pixel matches. Nearest neighbor ‘loses’ columns and rows of data and quite possible will mess up with ‘pixel snap’ because it shifts each vertex independently, resulting in occasional enlarging and shrinking effects, which would ‘pop’ rows and columns in and out noticeably. Is this the only choice? Do I have to settle for less than ideal and just accept it?

I guess the aim is to preserve the quality of pixel art as much as possible, and sometimes a single pixel disappearing can seem to affect shapes and details quite a lot. But maybe it’s just mathematically impossible to deal with this without hand-drawing art for every unique resolution.

One big issue I see is if you try to draw 1080p pixel art at, say, 1440 or 1280, it’s going to skip some pixels with nearest neighbor. I confirmed this with a test, every few pixels are skipped and the display doesn’t look very nice, especially any antialiased edges.

I suppose one choice might be this … in 1080p native mode, do nearest neighbor filtering and 1:1 pixel mapping. For 16:9 modes like 1920x1200, still do 1080p and add 2x 60 pixel black borders top and bottom i.e. 1080 pixel height ‘within’ 1200.

For a HIGHER resolution e.g. 2560x1440 or 4k, accept that graphics will scale up, OR that the native hardware display will use a ‘virtual 1080p’ resolution - and just accept however that looks whether it’s slightly blurred or whatever. OR, render to 1080p render texture and then upscale it in some kind of quality way. OR, create 4k graphics assets if you really care about quality. For 4k (3840 not the 5k mode) its an exact doubling of 1080p so it should look crisp.

In any LOWER resolution, render everything to a 1080p render texture and then redraw the whole screen using that texture with bilinear filtering applied? The display would be a little fuzzyish, but maybe we could apply bilinear in a shader (i.e. 4 samples per pixel and pixel mixing within the shader) and then apply a gentle sharpen filter kernel before output? That might crispen it up a bit (and might require more samples). This might possibly look better than just drawing everything as-is and letting the hardware figure it out, because filtering only applies ‘inside’ the geometry and not across the edge boundaries, so it should antialias better kind of like an antialiasing supersample filter? Only downside to rendering always at 1080p internally is the extra fill rate which means using 720p for ‘faster graphics’ will not get the benefit.

I was thinking to use a full-screen render texture anyway for post-processing purposes so this might work well.

I guess we have to accept that there is simply no logical possibility to use one graphics set on multiple resolutions if those resolutions aren’t exactly multiples of the artwork resolution. HD and Ultra HD work fine. But all others will have to sacrifice SOME quality because there is simply no way to map the data. The only perfect 2d option is hand-made graphics for every resolution you’re targeting.

I’m not convinced this is true. What I would try to do, is to design the game around the requirement that it has to be playable with different camera zoom factors and aspect ratios. Design your logic to be pixel perfect for your ideal low-res resolution (e.g. 640 x 400), render to a RenderTexture and upscale to the final screen resolution. If you need a different aspect ratio or screen resolution, you change the zoomfactor of the camera and the resolution of the RenderTexture to match. On one screen 1 source art pixel will be stretched to 2 screen pixels, on another it will be 3 etc… To stay on even multiples, you need to adjust the amount of “game pixels” that your camera sees. To not make that problematic for the game, you need to keep that limitation in mind for your design decisions. Shootemups might be a bad fit for this kind of workaround. But e.g. an RTS should work fine imho. Also look at portrait-mode mobile shmups that have been ported to PC. It seems to be very common to just fill “dead” screenspace with decorative artwork. If you really want to do a shmup or similar, and need that fixed size and aspect ratio for your screen then you can choose a lowest common denominator for your “game resolution” and add a frame of artwork around it that compensates for different screen aspect ratios. If it’s well integrated with the HUD and the style of the game, most people won’t even notice I think.

That said, afaik the general consensus about such pixel errors is that “gamers don’t care”, as long as the art is good.

This might also interest you:
https://www.assetstore.unity3d.com/en/#!/content/65509

I could imagine you get a “good enough” solution if you just render the game pixelperfect to a RenderTexture and then stretch that to fullscreen with RetroAA on it. I don’t know for sure though. You should ask them here:
http://forum.unity3d.com/threads/retro-aa-antialiasing-for-pixel-art-and-voxel-games.412287/

Thanks for the feedback and ideas. 640x400 sounds too low-res for me. I am aiming for 2x2 pixels on a 1080p resolution, i.e. 960x540. I could see rendering AT that resolution and then just upscaling instead of downscaling, but I also want to take advantage of a higher resolution accuracy for certain special effects.

The zooming thing is not in line with the game I am making but I can see how if I was ‘ok’ with zooming (tonnes of resolutions) I wouldn’t be so concerned with pixel perfect.

I’ll check out those assets, thanks.

1 Like

Let us know what solution you end up with, maybe even post a screenshot. I’m interested to see where it’s headed. Good luck!

What I decided to go with over the weekend is a) 1920x1080 resolution display rendered straight to a Render Texture. The render texture is 2048 square so I’m not using all of it. b) my artwork is going to generally be drawn at a 2x2 pixel resolution which means, 4 pixels will be the same color - it’ll look like a 960x540 display but its actually rendering at 1080p. A couple of reasons for this - one is that scrolling is much smoother at a higher resolution of precision, also I have an area of the screen which is a scanner/radar and I want that to be 1:1 pixel resolution. Eventually the render texture is rendered to the full screen at the end, allowing for image effects as well, and is also currently bilinear filtered so that if the resolution is higher or lower it will fairly smoothly scale, sort of acting like an antialiasing filter. This way I can get the display to draw all the details I want from the artwork and then nice and smoothly/evenly scale it down to fit the display. I will probably dabble with a custom shader at some point to tweak the quality.

I just ran into the dreaded half-pixel-offset of DirectX on windows versus OpenGL on mac. Had to tweak a heck of a lot of stuff to try to fix this. Seems that the ‘pixel snap’ macro on the sprite shader fixes it at least on windows.

Can someone at Unity confirm that the pixel snap feature is platform/API-sensitive and will snap to .5,.5 offset on DirectX and .0,.0 on Mac?

In my tests is appear that pixel snap does behave properly on Mac, i.e. both windows and mac projects output correct exact whole pixels when pixel snap is enabled. Kudos Unity.

One other issue though, and I don’t know if anyone else noticed this … I have a 2560x1440 display. I run my game in 1920x1080, which stretches to fit the screen. Now, I understand that at some point the hardware is going to upscale the graphics to fit which means probably a bilinear filter or something. However, what I found is, when I try to take a screen grab of the display (Command-Shift-3), it grabs the Unity screen but it grabs a 2560x1440 image, even though the res I asked for is 1920x1080. When I look at the grabbed image pixels, it looks like the image has been bilinear upscaled to 2560. It seems like Mac OS X itself actually takes the unity output and bilinear upscales it and then outputs to a 2560x1440 screen, all internally within the API and not so much ‘in the graphics card’ as such, and then the graphics card just always outputs native display resolution? I always thought the upscaling took places in the monitor display itself not in the graphics API or the visual layer of the OS. Anyone else confirm this? It is thus difficult for me to get proper screen grabs of my game at 1080p, other than in the editor (which luckily fits into the 2560 display fully with room to spare). I thought maybe the pixel snap thing wasn’t working because on examining the grabbed image it was bilinear filtered and I couldn’t seem to get rid of the filtering, but then I realized the entire display is getting upscaled automatically. Its a pity we can’t post-process the display to adapt it. I guess we could open a native display resolution and then handle up-scaling OURSELVES within the game, maybe with some better quality upscale algorithm? Anyone tried that? Is this how all monitors behave nowadays?

I don’t have a mac, but on NVidia cards on Windows I’m pretty sure that the user can choose where the interpolation happens.

I’m probably misunderstanding (edit: definitely did, I’ve just woken up, sorry), but imho you can do that. You just have to do your 1080p res rendering to a RenderTexture, and then do your magic to that when you upscale it to the screen resolution. You could make an image post effect that reads the screen resolution and based on that uses a different shader for example to render it to the screen. Or just blit it to render textures of different sizes and choose the filtering mode depending on what looks best.
If you are ok with some AA you could just render at native resolution, blit down to half res using trillinear filtering, and blit again to full res with point filtering, to get a pixelated look. That’s pretty much what I do with my 3D project. You can run it through a custom filter to apply a dithering effect while you’re at it, if you want.
I haven’t implemented it yet, but I’m planning to read out the screen resolution and based on that choose the factor at which pixels get “pixelated”. Large resolutions get bigger pixels and small resolutions might not need pixelation at all.

1 Like