# Making a pixel-perfect viewport in LibGDX

My game that uses LibGDX as the graphics library, should be playable with every display. Here's the problem: It have to be pixel-perfect. That means, the scale factor must be an integer value, so that my pixels do not stretch over two physical pixels - resulting in flickering pixels which I obviously do not want.

The player should see a constant amount Y of vertical space. The width is calculated by the screen resolution. My game field is designed for 4:3. Therefore a player with 16:9 or even 21:9 should see more in the horizontal space. Best example: An usual Pong field. The field is 4:3. A player with 16:9 should see the same field size, but with more "empty" space on the sides.

I've tried various approaches to achieve this. The first was the ScreenViewport, provided by LibGDX. Problem: Adjusting the unitsPerPixel would result in a smaller playfield. Or even mirroring it. I could provide a high-res playfield and scale it down with unitsPerPixels, but it is not very future-proof (Think of providing a 4k field for a future 512k monitor).

My second approach was a custom viewport. (No worries! It is written in Kotlin)

worldWidth *= (screenHeight/worldHeight).toInt()
worldHeight *= (screenHeight/worldHeight).toInt()

setScreenBounds(
screenWidth/2 - worldWidth.toInt()/2,
screenHeight/2 - worldHeight.toInt()/2,
worldWidth.toInt(),
worldHeight.toInt() )


It tries to scale up the field by a integer number (making it pixel-perfect). Problem: The unscaled zones (that would be normally covered by the non-integer-scaling) are black. Black bars. Instead, I want to show the game world.

Any ideas how to achieve this? Thanks!

## 1 Answer

Since Libgdx uses openGL to render using pixel perfect values is irrelevant since openGL converts all these units into a 0-1 range. The flickering issue you speak of is likely being caused by the texture filtering method you're using or how you are rendering the images.

You could however try to use the extendViewport and set your game field to the height of the current screen then the ExtendViewport will add the extra area when required.

The ExtendViewport (source) keeps the world aspect ratio without black bars by extending the world in one direction. The world is first scaled to fit within the viewport, then the shorter dimension is lengthened to fill the viewport.

• There's nothing about OpenGL's 0-1 range that makes it immune to distortion and filtering artifacts that can make pixel art deviate from the desired pixel perfect appearance. – DMGregory Apr 12 '18 at 3:45