Search Unity

Pixel Perfect Camera - Optimization for seamless camera movement

Discussion in '2D' started by Freigeist1337, Feb 16, 2021.

  1. Freigeist1337

    Freigeist1337

    Joined:
    Jun 25, 2016
    Posts:
    10
    Hi everyone!

    I am working on a 2D Pixel-Art game using the pixel perfect camera component with "upscale render texture" checked - which i seem to need to keep my particles and rotating objects pixel perfect.
    What i do not like about this however is that the camera movement now snaps to pixels (i understand why this is happening but want to get rid of it).

    During my research i found out about a technique how to achieve this in gamemaker and i was wondering how i could translate that to unity. As far as i understand they resize the camera output texture to screen size + 1px and then they move the camera in whole pixel amounts and for subpixel-movement they offset the output texture instead.
    Here is the link to the tutorial.

    My idea on how to achieve this for unity camera (with URP) was to modify the viewport rect of the camera but unfortunatly that does not seem to work with the pixel perfect camera component.

    The following code shows no effect when the pixel perfect camera component is enabled but it does work when it is turned off:

    private void LateUpdate()
    {
    viewportXOffset += 0.001f;
    myCamera.rect = new Rect(viewportXOffset, -0.1f, 1.1f, 1.1f);
    }



    Do i have to render my camera output to a rendertexture which i then offset instead? Wouldn't that be pretty performance heavy (i am targeting mobile devices)?

    I really hope somebody can help. I spend weeks on my Camera-Script already and now i am all out of ideas.

    Best regards and thanks in advance.
     
  2. Freigeist1337

    Freigeist1337

    Joined:
    Jun 25, 2016
    Posts:
    10
    Here is a code snippet s a starting point and to make my question more clear. I want to make something like this work:

    Code (CSharp):
    1. private void Start () {
    2.         float viewportXSize = (myCamera.pixelWidth + 1f) / myCamera.pixelWidth;
    3.         float viewportYSize = (myCamera.pixelHeight + 1f) / myCamera.pixelHeight;
    4.     }
    5.  
    6.     private void FixedUpdate () {
    7.         //Positioning stuff...
    8.  
    9.         unroundedPosition = new Vector3(x, y, cameraContainer.position.z);
    10.         roundedPosition = new Vector3(RoundToNearestPixel(x), RoundToNearestPixel(y), cameraContainer.position.z);
    11.         Vector3 roundedOffset = unroundedPosition - roundedPosition;
    12.  
    13.         cameraContainer.position = roundedPosition;
    14.         myCamera.rect = new Rect(roundedOffset.x, roundedOffset.y, viewportXSize, viewportYSize);
    15.     }
    16.  
    17.     public static float RoundToNearestPixel(float unityUnits)
    18.     {
    19.         float valueInPixels = (Screen.height / (myCamera.orthographicSize * 2)) * unityUnits;
    20.         valueInPixels = Mathf.Round(valueInPixels);
    21.         float adjustedUnityUnits = valueInPixels / (Screen.height / (myCamera.orthographicSize * 2));
    22.         return adjustedUnityUnits;
    23.     }
    24.  
     
  3. Coin9

    Coin9

    Joined:
    Feb 14, 2019
    Posts:
    16
    Freigeist1337 likes this.
  4. Freigeist1337

    Freigeist1337

    Joined:
    Jun 25, 2016
    Posts:
    10
    Thanks for your suggestion and for sharing the link.
    I tried the script and it show's no effect in my project. Unity API states that "OnRenderImage" is not supported when using Universal Render Pipeline and that ScriptableRenderPass should be used instead. However i can't find a simple example script that shows how ScriptableRenderPass should be used... :confused:

    There is also the FinalBlitPass that might be relevant for this?

    Anyway after 1/2 hours after reading about all this stuff i'm done for now and may get back to it later.
     
  5. Freigeist1337

    Freigeist1337

    Joined:
    Jun 25, 2016
    Posts:
    10
    After a lot of research i (kinda made it work). Screen texture is not yet scaled up correctly but the camera movement is nice and smooth.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. //using UnityEngine.U2D;
    5. using UnityEngine.Rendering;
    6.  
    7. public class SmoothPixelPerfectCameraMovement : MonoBehaviour
    8. {
    9.     public UnityEngine.Experimental.Rendering.Universal.PixelPerfectCamera pcc;
    10.     private Vector2 viewportScale;
    11.     private RenderTexture tempRenderTexture;
    12.     private Camera myCamera;
    13.     private int width, height;
    14.  
    15.  
    16.     private void Start()
    17.     {
    18.         myCamera = Camera.main;
    19.         SetWidthAndHeight();
    20.  
    21.         RenderPipelineManager.beginFrameRendering += FrameRenderingStart;
    22.         RenderPipelineManager.endFrameRendering += FrameRenderingEnd;
    23.     }
    24.  
    25.     private void OnDestroy()
    26.     {
    27.         RenderPipelineManager.beginFrameRendering -= FrameRenderingStart;
    28.         RenderPipelineManager.endFrameRendering -= FrameRenderingEnd;
    29.     }
    30.  
    31.     private void FrameRenderingStart(ScriptableRenderContext context, Camera[] cameras)
    32.     {
    33.         if(width != Screen.width || height != Screen.height)
    34.             SetWidthAndHeight();
    35.  
    36.         tempRenderTexture = RenderTexture.GetTemporary(width, height, 16);
    37.        
    38.         //you only need to change texture format if you want to use HDR colors e.q. with Glow Post Processing
    39.         if(SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGB111110Float))
    40.         {
    41.             tempRenderTexture.format = RenderTextureFormat.RGB111110Float;
    42.         } else { Debug.Log("RenderTexture format not supported"); }
    43.  
    44.         myCamera.targetTexture = tempRenderTexture;
    45.         Graphics.SetRenderTarget(tempRenderTexture); //dont think this is actually needed
    46.     }
    47.  
    48.     private void FrameRenderingEnd(ScriptableRenderContext context, Camera[] cameras)
    49.     {
    50.         myCamera.targetTexture = null; //blit directly into render buffer
    51.         Vector2 screenWorldBounds = new Vector2(myCamera.orthographicSize * 2 * Screen.width / Screen.height, myCamera.orthographicSize * 2);
    52.         Vector2 direction = new Vector2(Mathf.Round(transform.position.x * pcc.assetsPPU) / pcc.assetsPPU - transform.position.x, Mathf.Round(transform.position.y * pcc.assetsPPU) / pcc.assetsPPU - transform.position.y);
    53.         Graphics.Blit(tempRenderTexture, null as RenderTexture, Vector2.one, -(direction / screenWorldBounds));
    54.  
    55.         RenderTexture.ReleaseTemporary(tempRenderTexture);
    56.     }
    57.  
    58.     private void SetWidthAndHeight()
    59.     {
    60.         width = Screen.width;
    61.         height = Screen.height;
    62.     }
    63. }
    64.  
     
    Last edited: Feb 26, 2021
  6. enhawk

    enhawk

    Joined:
    Aug 22, 2013
    Posts:
    833
    Unity does all this automagically now. Just check the pixel snap option in your sprite material.
     
  7. Freigeist1337

    Freigeist1337

    Joined:
    Jun 25, 2016
    Posts:
    10
    Why would a sprite setting influence the camera movement?
    Anyway pixel snap seems to be disabled on the URP sprite materials.
     
  8. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    I have also been wondering about this, and the post here actually confirmed my initial idea on how to go about doing this. But more I think about it, this should really be supported by the Unity... anyway, I am also gojng to try my work around soon.
     
    Viole likes this.
  9. enhawk

    enhawk

    Joined:
    Aug 22, 2013
    Posts:
    833
    Sprites that are not snapping to "pixel" (an xy position that is a division of the PPU) tend to move around in the camera view from pixel to pixel when moving the camera at slow speeds. Sprites that are positioned at an exact division of a unit will stay in place.
     
  10. Viole

    Viole

    Joined:
    Dec 29, 2015
    Posts:
    38
    Unity, please make it a feature. It is crucial for pixel-perfect-camera package. Crucial.
     
  11. kennyy_

    kennyy_

    Unity Technologies

    Joined:
    Apr 7, 2021
    Posts:
    96
    Hi @Freigeist1337,

    Can you trying using Pixel Snapping as the Grid Snapping option to see if this mitigates the issue you are having. If not, I'll add to our backlogs as a feature request.

    upload_2021-8-2_12-36-14.png
     
  12. Freigeist1337

    Freigeist1337

    Joined:
    Jun 25, 2016
    Posts:
    10
    I tried all Pixel Perfect Camera settings before diving into custom solutions but pixel snapping looks really bad imo when having small amount of PPU and slow camera movement, since the jumping between the pixels is highly noticable.

    As i said though, my script fixed the problem for me. However this seems to work with Unity Version 2020.1.15f1 only - at least i kept having the problem, when i updated to a later version (which might be connected to some changes with the render pipeline or sth)...
     
  13. Rocky_Unity

    Rocky_Unity

    Joined:
    Oct 13, 2017
    Posts:
    96
    I'd like to necro this post as a request as well.
    Pixel perfection with smooth movements is highly desired. This means sub-pixels are allowed! Pixel perfect looks awful.

    A game that does this well is Stardew Valley. There is no pixel morphing in that game from non-pixel perfect movements, however everything rests at a pixel perfect location. The camera is also allowed to move in sub-pixel movements which makes it feel super smooth.

    This is not possible with the current implementation of PixelPerfect components
     
  14. rarac

    rarac

    Joined:
    Feb 14, 2021
    Posts:
    570
    you guys are crazy, stardew valley is the farthest from pixel perfect you can get

    im always saying, stop obsessing with getting your game pixel perfect, its to the point you want to copy stardew which is not pixel perfect and you dont even notice, so why do you think your players will notice your game is pixel perfect when you dont even notice it for stardew

    check this if you dont believe https://imgur.com/a/zJSR5
     
  15. Rocky_Unity

    Rocky_Unity

    Joined:
    Oct 13, 2017
    Posts:
    96
    It does do something that prevents that "shimmer" effect though.
    You are right in that it is not exactly pixel perfect, but it also does something that does not cause the shimmer effect, as well as have a smooth camera. And that is something that we are all after.
     
  16. rarac

    rarac

    Joined:
    Feb 14, 2021
    Posts:
    570
    thats just called a normal camera

    to prevent the shimmer you just have to fix your ortographic size to a set value based on the ppu of your assets

    stardew valley also has a very stable camera, it doesnt bounce, it doesnt dampen, always centered on the character, or some other object

    if you ran stardew valley in windowed mode and sized it into a small window you would see shimmer too (not sure if the game allows you to do this though)
     
  17. Tobi2408

    Tobi2408

    Joined:
    Mar 27, 2021
    Posts:
    2
    Hello, I have the same problem. The script above helped me a lot, but I still get some error messages with it, for example, the scene view is black while the game is running. Can you send me a finished version of the script, that would help me a lot.
     
    Last edited: Sep 13, 2023
  18. Freigeist1337

    Freigeist1337

    Joined:
    Jun 25, 2016
    Posts:
    10
    Sorry but I abandoned the project a long time ago.
    I think I did not make any big changes after posting this here. I remember that the script stopped working when I tried to update to a later version of Unity. Is using the mentioned Unity Version 2020.1.15f1 an option for you?

    If so, i can provide you the script or the whole unity project to look into it.
     
  19. D0NT_

    D0NT_

    Joined:
    Oct 19, 2023
    Posts:
    1
    I'm trying to solve this exact problem; this is the closest I've come to a possible solution.
    If you could provide the project, or even just the script it would be a huge help.
    I'm new to Unity so I'm way out of my depth here, but I need to use upscaling for my project and I really don't like how jittery the camera feels.
     
  20. JuandavidGameYTZ

    JuandavidGameYTZ

    Joined:
    Mar 26, 2023
    Posts:
    1

    Did you solved it?