Jitter when using Pixel Perfect Camera with or without Cinemachine (with MRE)

Unity doesn’t seem to be able to handle Pixel Perfect Graphics correctly, no matter whether you use CInemachine or not. I’ve read other forum entries but none of them had solutions of they didn’t describe the problem correctly.

Problem:

When using Pixel Perfect Camera with Upscale Render Texture, there will be jitter when following a moving object. It doesn’t matter if the object is moved by physics or not (in my video examples I move the character with physics, but I also tested with moving a square sprite using its transform)

Issue vs Correct (might look bad becaue of compreesion)

Minimal reproducible example:

Download the file below, press play and observe. Camera is child of the moving player, you can unparent it and make a follow script, but the error will still be there. This is the simplest setup.
Jitter2D.zip (67.6 KB)

What have been tried:

I made sure the whole project is well configured. I followed Unity Manual, all my assets are 16PPU (my tilemap tiles arre 1616 and my bird sprite is 32*24 pixels (the spritsheet is 224216). I also tested using a 16*16 square sprite.

All the sprites have the pivor point mode set to pixel.

I tried using a correction script to snap cinemachine to pixel perfect positions after the body stage, but that didn’t work.

I tried to analyze what is happening by setting Time.timeScale to 0.1 from project settings, the camera seems to go backward and forward depending on the position. It seems to be rounding issues but I’m not sure because if it were that my snapping script with cinemachine should have worked.

It’s my first time using this forum so I attach the videos as a zip and also as an embed from drive:

Issue with Cinemachine

Issue also without cinemachine

Videos.zip (6.4 MB)

I’m aware that Cinemachine docs says that there are limitations when using Upscale Render Texture, alright… But why is the issue present with the normal camera too? Is it absolutely impossible to have a perfect camera with pixel perfect setup?

This issue is driving my crazy since pixel perfect graphics are important to me

2 Likes

I would recommend to isolate the issue first without plugins in a new project, maybe that way if you find it might be a bug you can so submit it
Or even having a better example so is defined how you do move the character and the camera

Done! I added a MRE, I’ll link it here too
Jitter2D.zip (67.6 KB)
:

Hi! tell me if this makes the issue “better” for you, or if I misunderstood what the issue was entirely.

Jitter2D_tweaked.zip (82.6 KB)

That helps! It makes the movement smooth but in way that I believe that it’s not correct although it’s a good step and I appreciate it. In your project URP wasn’t configured, so I tested your sine script changes on my original project and it still worked. I didn’t notice any more change.

The problem with this approach is that you modify the camera, I already tried the same but with rounding instead of flooring without moving the camera. My issue is that the jitter only happens when the camera has the pixel perfect component with upscale render texture and I think it shouldn’t. I don’t think it’s wrong to snap the moving entities to pixel perfect positions, but the camera shouldn’t be aware of that.

With this approach you can only fixed snap the camera to the player, which is already a great improvement not gonna lie, but you can make anything more since the camera is teleported to the player each frame.

I’ll try this approach in my project, because when I tried something similar it seemed to work but only on certain coordinates, moving the player to specific locations resulted in jitter again. I will also try to build upon your approach to get something similar to cinemachine working but pixel perfect. Maybe I can store the “target” camera position, snap the camera to the player and after that add the clamped movement vector.

I’ll update this forum if I manage to get it working, and if possible without modifying camera transform from a player script.

Thank you! ^^

Glad it helped, even if just a little!

A potential solution, that isn’t really a solution because (in my opinion) because it sort of goes against the purpose of using the Cinemachine System in the first place, is removing the Damping on the Cinemachine Follow.

image

But you could potentially maybe find a solution by tweaking the numbers or interfacing with it in any way? I wasn’t able to solve anything on the damping, it always jitters no matter the number. But you could maybe make “your own” own damping system by dynamically tweaking the offset? I find the follow offset to be much more friendly and, at least on a superficial look. I didn’t see any jitter when moving values up or down.

PS: I imagine you’re aware, but just for posterity, none of this “brainstorming” is denying the possibility of this being an issue/bug. I just don’t usually count on updates for Pixel Art most of the time lol.

Oh yep, using damping is impossible but it shouldn’t. If you use cinemachine setting the resolution to your pixel perfect resolution (640*360 in my case) that doesn’t happen. And what is worse and weirder. If with that resolution you use the pixel perfect camera with upscale render texture… The jitter appears again! That’s why I think it’s a bug, because it doesn’t seem to be related with the low resolution and discrete steps of the camera but rather a render error on Unity’s side.

I have to analyze Celeste to check if that game camera had damping, because if it has it then that means that should be possible to do.

Although damping is not important to me, getting a little if look ahead working is more than enough. That and avoiding having to modify the position of the dynamic entity in the late update as with physics entities that could result in unwanted extra collisions between frames.

I’ll update this thread if I find something more about all this

Celeste has some smoothing, I just recorded a clip and analyzed it. Or well, more that smooth follow it has a soft zone where the camera doesn’t follow, but it’s not a strict follow as in those games. I do cre about pixel perfect, I’m aware this make things more complex but just because it’s harder it sholdn’t be ditched, pixel pefect is an important part of the art direction of my game. Using pixel perfect assets doesn’t do the trick. I don’t care that much about subpixel movement, but let’s say I have a shader that distorts part of the screen, I don’t want pixels to be warped or distorted, I want to see those low res pixel moving as in Celeste.

While I understand your point I have to say that this thread is about discussing the camera behaviour when it has the pixel perfect component and the upscale render texture mode. Telling to just not try to do pixel perfect game is not helpful ^^

Yeah, Celeste “Game View” is actually 100% pixel perfect save the zooming in for cutscenes. That is done in post.
But it fully features damping and is snapped to grid (well, it’s actually rendered in native-res, then upscaled and the UI elements added). You can see it moving in “whole steps” once the camera is slow enough

But back to our issue, upon sort of tweaking as much as I could, the best solution I was able to find was:

  1. Snap the Sprites Myself, so no need for the Pixel Perfect (only to calculate the Orthogonal Size, then it’s disabled)

  2. And then a nifty little script on the camera that tracks the speed of the camera and the speed of the object being followed.
    2.1) I realize that once both the camera and the object are moving at a similar speed, things start jittering.
    2.2) I check if the speed difference is within a certain (low) value, and if is: I add the speed difference back to the camera.
    2.3) So it keeps whatever distance it was supposed to keep, but the micro-movements/jitter is gone.

  3. Sorta Optional but I then set the camera Render To Texture to Render at Native Res, Just to make sure.

But at this point, I realize I don’t really need Cinemachine so I simply took it out and did the damping myself.
In retrospect, I feel that Cinemachine is one of these very cool things that if it works perfect for you, go for it. But once it doesn’t, it’s probably better to make your own solution.