Page 1 of 1

Scalable Game Graphics

Posted: Wed Nov 16, 2016 4:42 am
by dvdfu
Hey all, I'm making a pixelized game with low game dimensions (288 x 180). The game window can be scaled 1x, 2x, and so on. The game is using love.graphics.scale(x) to render at different scales.

As a side effect, higher window scales have "smoother" graphics resolution, because they're able to render "in-between pixels". For example, if an object is located at coordinate 100.5, it will render at a rounded screen coordinate 100 (or 101). However, at 2x resolution it will render nicely at screen coordinate 201.

This is really good, but there is a problem with rendering at decimal pixels, which is explained quite nicely here. The tearing occurs and is a really big problem for me, but the only solution seems like rounding drawing coordinates (which gets rid of the smoothness I mentioned).

How do games get around this problem? The only solution I can think of is manually multiplying all draw coordinates and rounding them, and not using love.graphics.scale(x). But this seems like a horribly contrived, messy solution. I've also looked at this thread, but the solutions involve true upscaling, which doesn't preserve smoothness.

Screenshot for reference, at 3x resolution (the horizontal black rows in the background):
Screenshot from 2016-11-15 23-55-17.png
Screenshot from 2016-11-15 23-55-17.png (14.22 KiB) Viewed 6568 times

Re: Scalable Game Graphics

Posted: Wed Nov 16, 2016 5:13 am
by raidho36
Consider rendering to canvas at native resolution and then upscale to fit the screen using nearest filter. This way you don't get mid-pixel positions though, but you're not supposed to with this kind of graphics anyway. You also should use nearest filter for all other rendering to avoid non-integer coordinates filtering, which effectively locks graphics to pixel grid.

Re: Scalable Game Graphics

Posted: Wed Nov 16, 2016 5:25 am
by dvdfu
raidho36 wrote:Consider rendering to canvas at native resolution and then upscale to fit the screen using nearest filter. This way you don't get mid-pixel positions though, but you're not supposed to with this kind of graphics anyway. You also should use nearest filter for all other rendering to avoid non-integer coordinates filtering, which effectively locks graphics to pixel grid.
I actually have the native-render-and-upscale implemented, but I still get the bleeding issue (this time, it's even more noticeable when upscaled):
Screenshot from 2016-11-16 00-22-47.png
Screenshot from 2016-11-16 00-22-47.png (13.93 KiB) Viewed 6561 times
Also, nearest is set as the default using love.graphics.setDefaultFilter.

I'm curious as to why graphics shouldn't be used this way. What's the standard way for drawing upscaled sprites at pixel coordinates of finer resolution?

Re: Scalable Game Graphics

Posted: Wed Nov 16, 2016 6:04 am
by raidho36
Your aliasing problem is clearly that the sprite polygon samples outside of sprite texture when it's shifted by sub-pixel amounts, i.e. where polygon fragment is still present as renderable pixel but where actual texture slips away. You should pad your textures with 1 pixel strip same as on the edge, or from opposite edge if the sprite is to be tiled. Or maybe it's a bug in your code and you render it 1 pixel higher than it actually is.

It shouldn't be this way because that's the whole point. Back in the day the hardware was extremely limited, most notably that rotating, scaling and god forbid filtering were prohibitively expensive, so they weren't used. Sprites were rendered 1:1 to the screen, small display resolution meant that visuals had to be represented by tiny amounts of pixels so easily recognizable and high contrast shapes had to be used, and harsh color palette limitations made them use very few colors. Pixellated graphics is not good of and in itself, getting extremely low fidelity graphics to look good anyway is what gives it its charm. If you're not going for the same effect, might as well leverage modern GPU power and use high fidelity graphics.

Re: Scalable Game Graphics

Posted: Wed Nov 16, 2016 7:26 am
by s-ol
Why don't you floor coordinates to thirds? just do love.graphics.draw(quad, img, math.floor(x * 3)/3, math.floor(y * 3/3)).

Or write a function f2(x, y) return math.floor(x*3)/3, math.floor(y*3)/3 end, or monkeypatch the love.graphics functions to do it for you.

Re: Scalable Game Graphics

Posted: Wed Nov 16, 2016 8:32 am
by raidho36
That's what he originally suggested doing but wrote off as excessive.

Re: Scalable Game Graphics

Posted: Wed Nov 16, 2016 4:18 pm
by pgimeno
dvdfu wrote:I actually have the native-render-and-upscale implemented, but I still get the bleeding issue (this time, it's even more noticeable when upscaled):
Screenshot from 2016-11-16 00-22-47.png
When you draw at 1:1 on a canvas, you should round the drawing coordinates of the stuff you draw. Also, if you have any graphics that are not at 1:1, you can run into that problem.

You should get a pixel-perfect screen when you draw on a canvas at 1:1. If you don't, you need to fix it, rounding coordinates of the stuff you draw until you do. Once you have it, you can upscale. There are ways to get pixel-smooth movements when upscaling; the zoomed pixels don't need to be at coordinates that are multiples of one zoomed pixel. But that's a different issue that needs to be dealt with separately. First step is to get the 1:1 pixel-perfect screen drawn on a canvas.

That's if you don't want to handle bleeding by extending the sprites (which sounds like a bad hack to me, as it generates visually incorrect graphics).

(Edited to clarify I meant on a canvas)

Re: Scalable Game Graphics

Posted: Wed Nov 16, 2016 5:11 pm
by D0NM
dvdfu wrote:Hey all, I'm making a pixelized game with low game dimensions (288 x 180). The game window can be scaled 1x, 2x, and so on. The game is using love.graphics.scale(x) to render at different scales.
you can add extra outline pattern around your tiles.

or overlap tiles vertically / horizontally by 1 pixel.
Like they do with real tiles on roofs.

Image

It might be useful if you want more smooth scrolling
instead of rounding all the coordinates to integers.

Good luck with your nice looking game!!

Re: Scalable Game Graphics

Posted: Thu Nov 17, 2016 12:16 am
by dvdfu
pgimeno wrote:You should get a pixel-perfect screen when you draw on a canvas at 1:1. If you don't, you need to fix it, rounding coordinates of the stuff you draw until you do. Once you have it, you can upscale.
Just realized, I was drawing at 1:1 but without rounding coordinates. Just did so and everything draws correctly now.

After considering raidho36's post, I think upscaling at 1:1 would be the best solution, and that I should focus on making the game look and feel good at its original resolution.

Thanks all for the help!