Unity Pixel Art Camera

Pixel art is a very popular visual style for games, but displaying it correctly in a modern game engine is quite difficult. The look can fall apart aesthetically if not rendered the right way. I want everyone’s pixel art to display beautifully, so I’m releasing a free Unity Pixel Art camera that solves this problem elegantly.

For an example of my tool in practice, check out my recently released “GirlJail” jam game. This is a good demo showcasing the production reliability of this approach across iOS, Android, PC, Mac and Linux.

At the moment the common approaches to pixel art in Unity are based on just setting your camera’s size in perfect ratio with your art’s pixel size, then setting point filtering on every texture and hoping for the best. This is woefully inadequate for maintaining consistency and displaying pixel art well.

History

As per the name, pixel art came about because every individual pixel a low resolution screen displayed was carefully decided and hand drawn. On modern high resolution displays it is not practical to draw every single pixel. We need to come up with new methods of displaying pixel art that preserve it as a style, while keeping it a viable low-cost choice.

Key Goals

To render pixel art correctly in our modern 3D-based game engine, there are a number of principles a Unity pixel art camera should follow:

Display sprites without distortion

a comparison of distorted sprites and correctly rendered sprites

Simply enough, when we display a sprite, it shouldn’t be stretched or distorted. Most existing solutions focus on addressing this, but don’t try to solve any of the following points.

Enforce a consistent pixel size

A comparison of inconsistent pixel sizes and correct consistency

Failing to abide by this rule is by far the most common mistake visible in modern pixel art games. All pixels should be the same size. This consistency lets players eyes process the image comfortably, allowing sharp edges to not disrupt the experience.

Conform to a pixel grid

A comparison of sprites not aligned to a grid, and aligned correctly

Related to the consistent pixel size, pixels should all align to the grid, a pixel should never be able to half overlap another pixel. Either it occupies the whole pixel space or it doesn’t. If there isn’t a consistent grid, the visual result is inconsistent pixel sizes, even if your images maintain correct scale.

Pixels are the atoms of your game universe, they are quantised and it’s safest not to split them.

Retain position stability while the camera moves

An example of positional instability, compared with the desired result

The position of objects in the world should not be affected by the movement of the camera. In the warning example above, look at the shadow under the character or the fence relative to the dirt path; observe that as the camera moves, the sprites jitter out from their expected position.

Fill the screen in a predictable way

A comparison of methods of changing size and aspect ratio

When designing the screen composition of your game, you want a high degree of certainty about what will and will not be displayed on screen. You want to choose the resolution that works best for the experience you want to provide. Many Unity pixel art camera approaches prioritise multiplying pixels by integer values, resulting in a completely unpredictable composition based on the resolution of the screen.

So a larger or smaller resolution screen should still display the same composition, and a change in aspect ratio should add as few pixels as possible onto our design.

Avoid floating point precision errors

An example of floating point imprecision error

We want our pixel art to always display consistently, however because we’re not working in an old school 2D integer space, our viewport is all based on imprecise floating-point maths. Even if we carefully round our sprite positions, distortion can still occur when sprites are placed on certain pixel boundaries, it is critical to be aware of this and prevent it.

The Solution

So how do we create such a consistent display of pixel art while also scaling to any resolution or aspect ratio?

Simply by rendering at the true designed resolution of our game, then scaling twice.

  • First we set up an internal texture of the ideal pixel resolution. The size of this is determined by an input target resolution, with pixels added on either the width or height to accommodate the screen’s aspect ratio difference, if any. The whole scene is rendered to this small texture. This step automatically eliminates any possibility of broken pixel grids, or inconsistent pixel sizes, because it is impossible to draw to anything smaller than a pixel.
  • Secondly we want to scale to any possible percentage, but scaling pixel art by non-integer values often leads to obvious distortion, or blurriness. To solve this problem as a first step we scale by integer values, using point filtering up to the first resolution larger than our screen resolution.
  • Finally, we scale this large texture back down to our destination screen resolution using bilinear filtering.

Update (April 2018): In version 2 of this package, I achieve the upscaling/downscaling effect in a single step via a bilinear-upscale shader, which looks almost identical but saves a significant amount of memory and GPU time on high resolution devices.

The result preserves the clean pixel look, while allowing us to target any resolution or aspect ratio we want. This has the side-effect of introducing some interpolated pixels at pixel grid boundaries, but with the high DPI of modern screens, it is not a visible issue. Take this image as an example (your browser will apply bilinear downsampling to it as it displays):

a high resolution image simulating the Unity pixel art camera bilinear reduction

But we’re not done yet, we still need to prevent sprite distortion and keep positional stability etc. The other major part required for this is a sprite shader to keep all the textures drawing at precise pixel positions. Inside this shader:

  • the first step is to effectively align the camera to a pixel position, so all vertices are offset by the position difference of the camera to the nearest pixel. This is the key to ensuring stability.
  • Then they are snapped to the nearest pixel in viewport space, so boundaries will sit on pixel edges and draw without distortion.
  • And finally offset by half a pixel if there is an odd-numbered pixel resolution as viewport space is always aligned to the centre.

Download

You can get the Unity Pixel Art Camera including an example scene from github, or download the unitypackage.

It requires a minimum Unity version of 2017.2. If you’ve tested it and it functions as you want, but you need it to work on an older version, let me know and I might invest some time in making it backwards compatible.

If you end up using this, I’d love to hear about your game (and if you’re nice I always appreciate a credit!)

Setup

It’s super easy to get started:

  1. Put the PixelArtCamera component anywhere in your scene (I recommend on your camera)
  2. Connect the Camera and Canvas in the appropriate fields if they’re not automatically filled in.
  3. Set the resolution on the PixelArtCamera component to your preference (and match the Pixels Per Unit to your texture imports, if you haven’t left it on the default ‘100’.)
  4. Put a material using the ‘Ocias/Pixel Art Sprite’ shader on your sprites.

That’s it!

A Personal Recommendation

A major point of contention in pixel art display is the choice of displaying pixel art with hard edges or smoothly interpolated. Generally the consensus has been on hard edges, despite the fact that pixel art at native resolution is realistically not as sharp. Why is this? We know sharpness is not authentic, and possibly not even optimal, but on some level we just feel smooth pixel art looks “wrong”. I propose there a very simple reason for this. We dislike pixel art when interpolated due to incorrect gamma color blending.

Consider these three images:

  • a gamma blended image
  • a photo of native 1 to 1 pixel display of the scene on my monitor
  • and a linearly blended image:

a scene using incorrect gamma interpolation

A photo of an LCD screen displaying a 1 to 1 pixel perfect pixel art scene

A correctly linearly blended pixel art scene

It is completely clear how far away the gamma blended version is from the ground truth. Black lines become much thicker, characters especially appear less pleasant to look at. When blended correctly – linearly – pixel art captures the same warm feeling we like from native display.

My recommendation: For your next game consider setting your color space to linear, and your pixel art camera to “smooth”. It’s actually more authentic and, in my view, more pleasant to look at than sharp pixels.

54 thoughts on “Unity Pixel Art Camera”

  1. It’s frustrating that Unity doesn’t come with something like this out of the box. For an engine that’s so popular for indies, it’s crazy how making a clean looking pixel art game that scales well isn’t immediately intuitive.

  2. Alex,
    I’m trying to use these scripts and they seem to work pretty well, however I noticed that changing scenes seems to somehow break the script. If I load a scene directly in unity it looks great. However, if I load the scene from another scene, then the pixels are different sizes.

  3. This is a fantastic tool. I was using another setup like the common approach you described, and it was leading to a lot of code for handling specific cases in order to keep things looking right.

    While setting this up, I encountered an issue with the clarity of the resulting texture. My sprites were originally set to 16 ppu, and while at that setting, there seems to be quite a bit of blurriness. If I increase it to, say, 100 ppu, that is resolved. I assume this issue is due to the pixel interpolation at grid boundaries that you described. Is there any way to mitigate this problem for lower ppu values?

  4. Thanks Oak! The PPU should have no relation to blurriness when set correctly, it’s just for making your unit scale correct. When you set the PPU, you need to make sure all your images have that PPU value in their import settings AND the pixel art camera script has that same PPU set. The pixel-boundary blur should be very minor, and not noticeable at native resolution.

  5. Hello Alex, I use your script but i have a problem, in my canvas have a button with OnDrag Event, when i Drag the button, this “Disappear” moving in others positions x, y , z very far from the canvas, I try in other scenes without your scripts and works, i need to solved this, Thanks

    public void OnDrag(PointerEventData eventData)
    {

    transform.position = Input.mousePosition;

    }

  6. Hey Erick, when setting the position of an object on a canvas it’s always safest to explicitly convert from screen space to canvas space, and since my system puts the canvas in camera render mode with canvas scaler settings, you must do this to get the correct position. The fastest way to fix your problem is to just set your transform to local space, then divide by the canvas’s scalefactor (and adjust 0 being the centre of the canvas):

    public void OnDrag(PointerEventData eventData) {
    transform.localPosition = (Input.mousePosition - new Vector3(Screen.width / 2, Screen.height / 2)) / GameObject.Find("Canvas").GetComponent<Canvas>().scaleFactor;
    }

    For a more thorough solution, use the RectTransformUtility class’s functions.

  7. Hey Alex, I really love this Asset it’s wonderful! I did run into a problem when it came using sprite masks. Sprites masks seem to not be rendered in the scene and I was curious if you knew a solution or if this was just a problem on my end. Thank you very much for this great asset!

  8. Hi Gavin, yep, I’ve fixed that right up for you, download the new package and enable the new “Require Stencil Buffer” checkbox to get your sprite masks working (turns out rendertextures don’t enable them by default.)

  9. Hey I’m curious about a few things. Sorry if these are really dumb questions I’m still new and don’t understand that much. When setting the “Target pixel dimensions” and “Pixels per unit” I assume these need to be set to the resolution at which the project is being made (1920×1080 in my case) and the ppu of the sprites used (16 in my case). When running the game in a higher resolution things seem to work fine, but when I set the resolution of the game to something lower like 1280×720 all the assets are no longer pixel perfect. Spacing of UI elements seems to break too? I have applied all the correct materials to everything in the scenes but perhaps there is something else I have overlooked? Thanks in advance.

  10. Hey Ser Oleg, the target pixel dimensions are for the internal resolution you render/design at, not the final screen resolution, everything is drawn at those dimensions then scaled up appropriately, that’s you’re seeing that result. See the included example scene for reference.

    This is a tool for rendering traditional low resolution pixel art games, if you’re working with assets created at 1920×1080 and just want them 1-to-1 physical-pixel-aligned, you can just configure a few built-in settings instead to achieve that, check the instructions on the Unity blog.

  11. Hey Alex! Awesome article! It’s very informative but I still don’t get many aspects of the pixel-art approach. According to your latest comment: What do you mean by “internal resolution you render/design at”?

    Basically, my problem is… How do I start a pixel-art game project? What decision should I make at the beginning to not screw things up?

    I’d love to hear an answer from you. Have a great day!

  12. Hi Jacob, don’t worry too much about screwing things up, the best and normal way to learn is to make mistakes.

    That said, the key to understand is that it’s about having an internally consistent universe of pixels.
    When making a pixel-art game, think about it like you were making a Gameboy Advance game. A GBA screen is 240x160px, so you design your screen composition, do your mock-ups and draw your sprites etc for that resolution. So broadly, early on you want to pick a resolution for your game. Generally the lower you go, the better, especially for projects that you’re doing solo or on a small team (going above SNES resolution, 256×224, gets pretty time consuming). This tool will then take your low-resolution pixel art and make sure that 240×160 or whatever resolution style you’re creating will display the same whether it’s on a 1920×1080 screen or 2880×1440 screen or anything else.

    If you’re starting out, it may help to visit Pixeljoint and Pixelation to get familiar with some principles of the artform.

  13. Thank you very much for your answer! I think you’re clarifying things up for me. I’ll check out those communities to gather more knowledge in this topic. Have a nice day!

  14. Hey, Awesome tool. I ran into a problem though.

    After launching the sample scene, I changed color from gamma to linear as you suggested, after a little while (after playing the game once? or resizing the viewport…) the game screen gets really washed out. After a while other unity windows (like scene) also look bleached. The problem is resolved on swithcing back to gamma color space. Any idea whats going on?

  15. If you’re getting changes in colour in linear space other than in blended areas, it usually means you’re running into bugs in the Unity editor. I’ve also run into the problem of UI etc also get transformed to linear colour in my non-pixel-art projects. Unfortunately this is probably not something that my tool is causing, so I can’t fix it for you. Try restarting Unity, building your game and check if it renders as expected there, and maybe try a different Unity version if this is a critical issue for you.

  16. Thanks, it seems on further investigation you are probably right. I always hate bugs that can’t be fixed because they aren’t your fault, haha.

  17. Thank you for solving my last issue! I’m also curious what licence this falls under?

  18. Of course! I’ve made sure to credit it “Alex Ocias’s Pixel Camera” in all of my games that I’ve used it in! Do you have another preference to how you wanna be credited?

  19. Hello,,, im using your great assets for my last project,, thanks ^^ … but now im using canvas on diffrent HUD scene (embed scene) … it seems doesnt work as expected… should i use pixel art script on both scene?

    scene 1 has main camera
    scene 2 has canvas

  20. Hi Wednesday, for now, just temporarily set up a canvas in scene 1, let the pixel art camera configure the settings of the canvas scaler, then copy the values onto the canvas in scene 2. Alternatively plug in the canvas to the pixel art camera when scene 2 loads.

    I’ll look into changing the way canvas setup works to make this less of a workaround in the future.

  21. I followed your steps and everything seems to work well, save for one issue: my sprites constantly jitter out from their positions during camera movement, just as you’ve demonstrated in one of your goals. How can I prevent this? I currently am working with a PPU of 16 and have my camera attached to my player, who moves along a grid similar to Pokemon.

  22. Downloading the latest version fixed the jittering for my player, but the issue persisted for other sprites that did not move with the camera. Then I realized I forgot to apply PixelArtSprite to my tilemaps. Doing this fixed the remaining jittering up. Thank you so much!

  23. I just can’t seem to get it to work, in the example scene it works fine. I can drop my own sprite in and everything works great, but when I try using it in my own scene it doesn’t work, I add the pixel art camera component, add the canvas and camera to it then I add the shader to my sprite but nothing really happens. I noticed that in the example scene, pixel resolution, upscaled resolution and pixel scale all change whenever I change the screen size but in my own scene they stay the default and never change. Upscaled resolution actually sits at 0x0, I’m not sure what I’m doing wrong…

  24. Hey Ocias! excellent asset, I’m working on achieving perfection and I have some doubts regards your asset.

    How do I set my UI not blurry? I can sense some pixel-blurryness even tho I followed every step and I have example scene, I can’t even replicate your HUD in your own example scene. Maybe I’m missing something? thanks in advance!

    I mean in my own example scene* I would just like to achieve more sharpness in my UI.

  25. Hi Ezekiel, sounds like an unusual problem… Can you double check that you’ve got your reference-resolution and pixels-per-unit set up appropriately? What values are you using?

  26. Hi Jes, I think I know what you’re running into. There’s a special PixelArtUI shader/material included to help with this problem, are you using it on your UI elements?

  27. Hello. I just discovered this asset, and it’s quite impressive! I’ve spent a lot of time on this problem without getting good results.

    I wanted to ask about the PixelArtDiffuse shader and its usage. Specifically, I’m looking for a substitute for Unity’s Sprites/Diffuse shader that works with this pixel art camera. PixelArtDiffuse appears to be designed for non-sprites since it includes a texture field and is used on a cube in your example scene. If I try creating a material with PixelArtDiffuse and assigning it to a sprite, the transparent sections of the sprite are rendered as black. I did try using the standard Sprites/Diffuse shader, and even though I didn’t notice any immediate problems I’m assuming this isn’t a good solution due to the precision issues you mentioned.

    Is there a proper way to use PixelArtDiffuse with sprites as a substitute for Sprites/Diffuse, or is this functionality not supported? Anyway, thanks for making such a useful asset package available!

  28. Hi Kevin, thanks! I’ve just added a new shader to handle that for you, grab the latest version from GitHub and use the “PixelArtSpriteDiffuse” material.

  29. Wow, thanks for such a quick response! I tried PixelArtSpriteDiffuse on a few different objects (with a light in the scene), but the sprite disappears completely with that material (it’s fine with the others). I’ve checked alpha settings on both the sprite and the material and they are both at 255. Any ideas?

  30. I’m not sure what would be causing that for you (got a Pixel Art Camera script active on your Camera, right?). If you’re still running into the problem, send me a repro project.

  31. https://www.dropbox.com/s/0xxh4mxzy9r722m/PixelCameraDiffuseTest.zip?dl=1

    Still having the issue. In this project folder, I took your example build and added PixelArtSpriteDiffuse to several objects in the example scene, including the two characters. If this is a system dependent problem, it might not be noticeable on your end, but I included a screenshot in the top level folder called “DiffuseScreenshot.png” that shows how it looks on my end. I’m on Unity 2018.1.0f2 on Windows 10 in case that matters. Thanks for the help!

  32. It appears you’re not using all the latest files, so the shader isn’t getting the values its needs from the script. Try updating to the latest version from Github again, and make sure the full directory is updated.

  33. Hello Alex, Thanks for quick response! I’m using PixeArtFont Material, just like your example. Canvas is like the script set it so I don’t see what I’m doing wrong, I’ll let a screenshot about my result here: http://prntscr.com/jqodsh

    (I’m having this issue with fonts and texts)

  34. Ah I see Jes, there’s something strange going on there. First download the latest version of the Pixel Art Camera if you’re not already on it, and if this persists, please send me a repro project.

  35. I see the problem Jes, you’ve got some broken values in your Camera’s Viewport Rect setting. Set them back to 0, 0, 1, 1 and everything will render correctly.

  36. Have you done anything like this before? maybe I’m missing something up again? how would you achieve this? I believe it’s the only thing missing for me to consider this framework perfect for me at least.

    Again, thank you for your help Alex. It’s hugely appreciated.

  37. I’m working on built-in Windowboxing (you can see it’s commented out in the script); there’s a really simple way to achieve it in the meantime: just add a panel to your canvas that’s the same width and height as your target resolution (no stretching settings), then put big black rectangles attached to its outer edges. This way you can also use fancy frame graphics if you need to.

  38. Alright, thank you, that’s actually a nice idea I’ve actually used in some engine called Construct 2. Thanks for letting me know you’re working on it. I’ll let you know if it works~ thanks!

  39. One question When i build my game and install it in my android phone why do the colors of my sprites look more darker then in unity

  40. Hi Rohan, it’s tough to say without knowing the specifics. The screen gamma of your monitor may be different than your phone, or it may be a gamut issue, or something else entirely. My suggestion is to take a screenshot of the game in both circumstances to compare on the same screen. You can email me that and any other details you have and I may be able to help investigate.

  41. Hi, I found your wonderful script a few weeks ago and it solved so many issues i can’t even thank you enough. 🙂

    However, I’ve noticed that the unity post processing stack v2 doesn’t work when your script is active. Is there any way to get it working or is the issue on my side?

  42. Hi John, I’ve seen someone else report to Unity that their PP stack doesn’t work my camera, I’m not familiar with the specifics of that package though. My recommendation is to try use what you need from the legacy image effects package.

Leave a Reply

Your email address will not be published.