Search Unity

The best pixel perfect method

Discussion in '2D' started by gillemp, Dec 18, 2017.

  1. gillemp

    gillemp

    Joined:
    Nov 23, 2015
    Posts:
    81
    [ Moderator Edit: Pixel perfect rendering is now available on Unity Package Manager (built in) https://docs.unity3d.com/Packages/com.unity.2d.pixel-perfect@1.0/manual/index.html ]


    Hi, I'm trying to choose which pixel perfect method should I use.

    I've seen at least 3 that seems to work pretty well but I don't know which one is the best in terms of performance and final effect.

    The first one is a shader: https://www.reddit.com/r/Unity3D/comments/7kdmpz/pixel_art_shader/

    The second one a trick with cameras:


    And the third one a small script used in the cameras:


    I don't know if you use any other method but any suggestion is helpfull, so share it with everybody ^^

    Thanks for you help!
     
    Last edited by a moderator: Jul 3, 2018
  2. SiegfriedCroes

    SiegfriedCroes

    Joined:
    Oct 19, 2013
    Posts:
    569
    I wasn't able to look at the videos completely but my way of doing it is having a script on the camera that lets you pick a resolution (for example: 160x144, Game Boy resolution) and it will automatically display that resolution as big as possible on screen keeping the aspect ratio and only using integer scale values:

     
    Ryiah, Amon and JoeStrout like this.
  3. gillemp

    gillemp

    Joined:
    Nov 23, 2015
    Posts:
    81
    Can you share the script to see how it works? Is seems to be similar to one of the options I've posted.
     
  4. gillemp

    gillemp

    Joined:
    Nov 23, 2015
    Posts:
    81
    After some testing the method that gives me less errror and better performance seems to be the seccond one:


    However, the problem that it has is that only works with one fixed aspect ratio.

    I've created a script to make it able to work in any aspect ratio of the screen creating more or les horizontal pixels but with a fixed number of vertical pixels.

    The script that I've made is this:

    Code (CSharp):
    1. /*
    2. *             gillemp            -         Triunity Studios      
    3. *   https://twitter.com/guplem  -  http://www.triunitystudios.com
    4. */
    5.  
    6. #pragma warning disable 0649
    7. using UnityEngine;
    8.  
    9. [RequireComponent(typeof(Camera))]
    10. public class MainCameraLR : MonoBehaviour {
    11.  
    12.     private int resolutionWidith = 1;
    13.     private int resolutionHeight = 144; //Height resolution of the screen
    14.  
    15.     private Camera cam;
    16.     [SerializeField]
    17.     private Camera virtualCamera; //camera that is son of the main camera
    18.     [SerializeField]
    19.     private GameObject virtualScreenQuad; //quad that is son of the virtual camera
    20.     [SerializeField]
    21.     RenderTexture defaultTexture; //Texture applied to the main camera and the material that is applied to the quad
    22.  
    23.     void Start ()
    24.     {
    25.         //Get the camera component
    26.         cam = GetComponent<Camera>();
    27.      
    28.         //Calculate the screen ratio
    29.         float ratio = ((float)cam.pixelWidth / (float)cam.pixelHeight);
    30.         //Calculate the resolution widith
    31.         resolutionWidith = Mathf.RoundToInt(resolutionHeight * ratio);
    32.  
    33.         //Set the propoer properties for the virtual camera
    34.         virtualCamera.orthographicSize = Mathf.RoundToInt(resolutionHeight / 2);
    35.         //Set the propoer properties for the virtual screen quad
    36.         virtualScreenQuad.GetComponent<Transform>().localScale = new Vector3(resolutionWidith, resolutionHeight, 1.0f);
    37.  
    38.         //Set the propoer properties for the texture
    39.         defaultTexture.width = resolutionWidith;
    40.         defaultTexture.height = resolutionHeight;
    41.     }
    42.  
    43. }
    I hope this helps anyone in my situation.
     
  5. SiegfriedCroes

    SiegfriedCroes

    Joined:
    Oct 19, 2013
    Posts:
    569
    I was thinking about making it a free asset :) But as there isn't that much interest It's not a priority right now. I'm developing a Game Boy style game using this script/asset so I'll probably release the asset when I release the game.
     
    gillemp and freedomize like this.
  6. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    Not quite sure what you are trying to achieve exactly. A low resolution blocky pixel type of display?
    What I’m doing currently is using a known size render texture, render everything to it, then scale it up. That will make absolutely all rendering become blocky, but it could also introduce some cross-pixel bilinear filtering stuff. You have to also position objects at exact integer coordinates.
     
  7. freedomize

    freedomize

    Joined:
    Aug 6, 2015
    Posts:
    30
    I tried to use this method. Unfortunately sprites begins to shake very much when they are moving. Have you encountered with a similar problem?
     
    Last edited: Jan 3, 2018
  8. gillemp

    gillemp

    Joined:
    Nov 23, 2015
    Posts:
    81
    Yes, exactly!

    How are you able to choose the original resolution and after scale it up? I could not find anything on internet except what I've posted...

    Thanks
     
  9. gillemp

    gillemp

    Joined:
    Nov 23, 2015
    Posts:
    81
    Yeah, and I don't know how to achieve a better result...
     
  10. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    I make a render texture of a given size and render to it always at that resolution. If the screen resolution is different then it gets scaled down with a separate camera.

    Lately though I'm doing it a little differently.

    I have a 640x360 virtual display resolution, IF the screen is HD 16:9 ratio. Either I make a render texture this size, or I make one at an exact multiple of this like 1280x720, 1920x1080, 4k etc. These are exact multiples. If a screen resolution isn't exact multiple vertically, I keep my pixels at exact sizes e.g. 1920x1080 has 3x3 pixels per cell. On 1024x768 for example, the closet I can get is 1024x720, with 2x2 pixel cells, and then I add some black bars at bottom and top. I am keeping the pixels-per-cell a whole value/multiple at all times. For 16:9 it works perfectly, for 16:10 it has a slight black bar top and bottom, and for 4:3 or 5:4 the horizontal width is a bit less and depending on the resolution it only increases the game display in multiples of 360 pixels height. A bad example is 1680x1050 which can't quite fit 1080 so drops to 720 and has some quite thick black bars, but not many people have that resolution. I'm also targeting desktop so I'm not really optimizing for iPads or 1024x768 2048x1534 type resolutions.
     
  11. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
  12. print_helloworld

    print_helloworld

    Joined:
    Nov 14, 2016
    Posts:
    231
    My method of doing is to render the camera view into a render texture, and render that texture onto the screen using GUI.DrawTexture with ScaleMode.ScaleToFit.

    My camera's ortho size is half of the vertical resolution. With these 2 things combined, you get this effect.
     
  13. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    I highly doubt that your approach will ever be PixelPerfect in any sense.
    You will still have both problems that people try to solve when refering to PixelPerfect:
    - 1 pixel wide jitter(EDIT: i meant Pixelmorphing) due to point filtering and movement (camera and|or objects)
    - blurry edges when scaling to different resolutions

    PixelPerfect can only be achieved if every factor is completely designed to match:
    - camera's orthographic size
    - screen resolution
    - PixelsPerUnit of each texture, scale on all SpirteRenderers
    - PointFiltering
    - Camera Anchor Position Snapping
    - Sprite Object Position Snapping (Sprite Texture-Center matters too)


    I took some hours and created a PixelPerfect showcase project for you guys, enjoy!
    Package removed: caus' no one cares :/
    (small caveat, diagonal movement in ArtPxSnap is Jittery with a CharacterController, i always recommend ScreenPxSnap)
     

    Attached Files:

    Last edited: Jun 14, 2018
  14. print_helloworld

    print_helloworld

    Joined:
    Nov 14, 2016
    Posts:
    231
    Have you seen the video or not?
    Pixel jitter will happen if the positions of the objects arent aligned, and blurry "edges" are fixed by point filtering.

    Which in my projects, I have both of those issues solved.
     
  15. Undertaker-Infinity

    Undertaker-Infinity

    Joined:
    May 2, 2014
    Posts:
    112
    Hi

    I've spent a few weeks hammering away at this problem for my game and will be very soon releasing an Asset that does it all for you, just type in what you want and it's ready to use (still have to setup sprites properly though).
    I'm using the Render Texture method, which seems to be the most efficient one and gives you a lot of flexibility.
    I also resize so that source pixels will never fall halfways into a screen pixel; No blurring, supports non-square pixels like Amiga/Atari 2:1 and you get to put things around the resulting image (a frame, UI, a background, whatever).
    I managed to get rid of the jitter, which can be caused by rounding errors relative to the camera. My result is a solid image, no jitter, even for slow moving objects and camera. How much work goes into it depends on what your game will be doing: there's no jitter if the camera doesn't move or if all objects are static and aligned to pixel. At worst, you'll need to use all of the provided scripts. Can't do anything about particles though, you'll see jitter there if they move slowly.
    Also managed to fit in customizable screen shake that does not make you dizzy.
    My camera is able to track transforms with a static call that does a configurable transition, so jumping between objects is really easy.
    I'm working on the video right now, I'm quite happy with the results though I'm struggling to find a version of Unity where tilemaps are not broken (srslywtf)

    I'll update with the video link when it's done.
     
  16. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Thanks for responding, You might have hard edges and no Jitter between objects, but if you zoom seamlessly you WILL have Pixelmorphing, i see that in a lot of games recently and i hate it. If you don't see this effect on your approach i am seriously puzzled because as far as i understand you cannot have free resolution AND hardEdges+noJitter+noMorphing (it's physically impossible)

    Look at those WEBMs to see what i mean, i zoomed in screenwise to make it very noticeable, enjoy!

    Pixelmorphing:


    Continue:
    Pixeljitter:

    BlurryEdges:

    PixelPerfect: My Approach set to Screenpixelsnapping, u can also snap to ArtworkPixels

    The jerkiness is the codec and my mousemovement, Download the above unitypackage to try the build. The only time where i get artifacts is during Zoom-Transitions. This is impossible to avoid but mostly not needed anyway.

    captured with https://github.com/TheTarkus/WebMCam/releases
     
    Last edited: Jun 11, 2018
  17. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Small Followup, i tried your projects, i love Top-Down shooters and i like where you are going with your projects! I also confirmed the morphing:


    I beg you to try my PixelPerfect script on the Nuclear Throne remake and set it to SnapToScreenPixel. I always got a headache because the original Nuclear Throne snapped to Artwork-Pixels, the game could have been so smooth. I would love to see it, here is my package again, if you need any help, feel free to ask me.

    Package removed: caus' no one cares :/
     
    Last edited: Jun 14, 2018
  18. print_helloworld

    print_helloworld

    Joined:
    Nov 14, 2016
    Posts:
    231
    For the first yes, I didn't fix, but for the second one that will always happen if you scale out of proportion because the pixels have to go somewhere. If the screen was at its base resolution, or x2 or x3, then it wouldn't show those effects. Thats just how rendering works.

    Thank you for checking out my games though, I appreciate that.
     
  19. Undertaker-Infinity

    Undertaker-Infinity

    Joined:
    May 2, 2014
    Posts:
    112
    Recording video I bumped into issues. I think I'll have to rewrite some stuff.
     
  20. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Yes, this is why i argue about the morphing. If someone has 1440p instead of 1080p your game should recognize it and adapt some values. Enter the Gungeon has very informative Settings menu on that part that is totally aware of the viewport resolution, view it as the gold standard in this regard.
    My scripts can help to achieve that.
     
  21. Undertaker-Infinity

    Undertaker-Infinity

    Joined:
    May 2, 2014
    Posts:
    112
    How does this look?
     
  22. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Really nice depiction of the problem man!


    Snapping to the artworks pixels is ok on games that have mostly still screens or fixed camera movement speed. E.g. Mega Man where his running (dashing, dog-flight) speed is translated 1:1 to the camera when he moves. So the cam translates a constant amount of pixels each frame which looks neat and this is what most nostalgia games did. Also the interlaced Video mode of the CRT-screens helped those games to smooth out jagged movement by virtually moving the camera only half a pixel between frames


    But when you have 'smooth' slow camera movements on todays 60fps screend, the pixel snapping looks really S***ty in my opinion, and a game only improves if you smooth that out.
    Because when you use pixel-art and achieved pixel perfection you will have a case where 1x1 artwork pixel always consists out of 2x2, 3x3, 4x4... screen pixels. I believe it is almost always better to snap to those pixels instead except if you need the camera directly sticking on an artwork-snapping character (because then the character would bounce back and forth some screen pixels while the camera pans)

    Have you ever played nuclear throne? I love that game, but the jagged camera behavior when you move your mouse is always giving me headaches.

    EDIT: Do you also have a fix for that staircase effect? You know, when you snap to diagonal movement, the camera will often perform two orthogonal movements in different frames instead of a diagonal movement once, i can see that in your video
     
    Last edited: Jun 14, 2018
  23. Undertaker-Infinity

    Undertaker-Infinity

    Joined:
    May 2, 2014
    Posts:
    112
    Thanks! it's hard to convey the problem and even more in screenshots, which I'm doing now. Resizing in an image editor brings the problem back!

    I haven't played nuclear throne, but I know what you mean.
    I intended this asset for a VERY retro look, say, Gameboy or Atari or even old mode 13h with it's non-square pixels. Scrolling was what it was, definitely not attached to a mouse though. When speed and direction are constant, it looks fine. This thing allows for adding finishing touches, so you could add motion blur or bloom or something on top. I haven't tried it yet, but I designed it for tinkering (the smooth zoom, for example, is one of those tricks that runs on top of the system). I do plan to add a helper for zelda/megaman scrolling, which should be really simple with what's already included (just a trigger that queues a transition to the new center).

    For snapping to screen pixels I believe you just have to turn on Pixel Snap in the material, have you tried that?

    I never thought of the issue you mention on diagonals, being used to it as I am in old computers. That sounds difficult.
    Perhaps it could be solved with a movement decimator for the camera? but then you could not keep a character perfectly centered though. Hmmm. I can't think of an example of scrolling as you mention even in old games, do you have any?
     
  24. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Actually i just did that today, it's just a small function that delays the SnapMovement by 10% of the pixel- or snap-Width.

    So the diagonal snapping is prefered when moving while orthogonal moving is only delayed by 10% of a single pixel's width. But see for yourself, its cleaner diagonal movement instead of staircasing, pretty noticable, but the video codec eats a bit of the red characters jittering


    non diagonal movement is also smoothed (green is staircasefiltered, orange is not):
    upload_2018-6-14_23-9-0.png

    However, i would not use staircaseFiltering for Artwork-snapped camera as this messes up the rate at which orthogonal movements are triggered when the factor isn't well adjusted
     
    Last edited: Jun 15, 2018
  25. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,154
    @Marrt

    Great work - true pixel perfect is much more involved and I think many just settle with some compromises. I offered unity a long time ago to work with them on a tutorial but it never happened and I am not so active anymore.

    I have not tested unities alpha/beta for pixel perfect, but they might be finally addressing it.

    Your implementation looks great, maybe you should wrap it up in a retro asset (a plan I once had) which includes things like pixel collision, advanced screen wrap, mod player, palette reduction, crt shader, etc...
     
    Marrt likes this.
  26. Undertaker-Infinity

    Undertaker-Infinity

    Joined:
    May 2, 2014
    Posts:
    112
    Oh I get it now. Slowing down the gif is a great feature btw.
    You're correct in that it messes up the rate (you can see it happening already). Is this preferable for some people? When pixels are tiny, you wouldn't notice anyway so it's only an issue for big pixels.
    I could make it an option.

    BTW would you call that video artwork snapping? because I would. Just checking.
     
  27. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Yes, whenever you snap to the pixels of your spritetexture, i refer to it as ArtPxSnapping. When you snap to fractions of artwork-pixels (because your screen uses more than one pixel to display it), i talk about ScreenPxSnapping.

    But you know what, i completely forgot i can just build a WebGL Build and let you guys try it:

    http://marrtgames.com/playerFiles/PixelPerfectRendering/
     
  28. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683
    this is new?
    2018-06-15 (2).png
     
  29. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
  30. Undertaker-Infinity

    Undertaker-Infinity

    Joined:
    May 2, 2014
    Posts:
    112
    oh boy, didn't know about that!
     
  31. zapposh

    zapposh

    Joined:
    Nov 12, 2016
    Posts:
    117
    This is absolutely what I have been waiting for all along! It works super well and is a breeze to set up.
    Well done guys!

    To anyone making 2D pixelart games, I highly recommend it!
     
  32. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Get it using Package Manager. No need to hunt around github and PM will allow easy in-unity updating.

    I edited the original post for new readers.