How I solved my biggest pixel art problem | Pixel Perfect — Transcript
Full transcript
- 0:04This is Callous.
- 0:07And now Callous has a big issue.
- 0:10They're trapped in a static world. One
- 0:13where the bounds of their existence is
- 0:15limited by what we can see.
- 0:20In the series so far, all of the
- 0:22examples we've looked at involved scenes
- 0:24with a motionless camera.
- 0:26>> [music]
- 0:27>> If we want to build worlds that people
- 0:29can explore, we need some way of moving
- 0:32our viewport in a pixel-conscious way.
- 0:34>> [music]
- 0:36>> Simply moving the camera as you would in
- 0:39a traditional game has issues. The
- 0:41low-resolution grid that our engine runs
- 0:44off cannot handle the high-resolution
- 0:46movement of our camera.
- 0:50In today's episode of Pixel Perfect, I
- 0:52want to build a camera system that can
- 0:54handle the responsibilities of moving
- 0:56through our worlds, dealing with
- 0:58sub-pixel movement, camera rotation, and
- 1:01interactive gameplay systems.
- 1:06If you enjoy the episode, a like and
- 1:08subscribe always goes a long way in
- 1:10helping the channel. It's free, and you
- 1:12[music] can always change your mind at
- 1:14any time. Thank you, and let's get into
- 1:17it.
- 1:28To understand what we're trying to
- 1:30solve, let's make the problem clear.
- 1:32>> [music]
- 1:32>> We have a high-resolution output that
- 1:34the user sees, and a low-resolution
- 1:36pixel scene [music] that needs to be
- 1:38rendered. The camera and pipeline is
- 1:40sort of the arbiter between these two
- 1:42endpoints,
- 1:43>> [music]
- 1:43>> and needs to decide how to translate the
- 1:45source image.
- 1:47Now, we covered this partially in the
- 1:49very first episode of the series when
- 1:51[music] looking at point samplers, but
- 1:53in those examples, it was clear how the
- 1:55source can be scaled up to full
- 1:57resolution.
- 1:58But now [music] we want to look at
- 2:00moving the camera.
- 2:01And this causes pixels to travel across
- 2:03boundaries, [music]
- 2:04which introduces new issues.
- 2:09To take a quick detour to look [music]
- 2:11at some existing examples, we can sort
- 2:13of categorize pixel art games into two
- 2:15distinct buckets. That is pixel perfect
- 2:18[music] games. These are which the
- 2:21underlying source image is rendered to a
- 2:23low resolution grid, and pixels can only
- 2:26move between cells on that grid.
- 2:28>> [music]
- 2:28>> And the second bucket is sub-texel.
- 2:31These are games which the assets are
- 2:33drawn as pixel art, but they exist
- 2:35[music] in the world using typical
- 2:37floating point transforms and can move
- 2:39smoothly anywhere on the screen. [music]
- 2:42In our project, we're firmly in bucket
- 2:44one. We have a low resolution grid,
- 2:47>> [music]
- 2:47>> and objects can only communicate their
- 2:49pixels by writing their colors to these
- 2:52limited cells.
- 2:54This is an important distinction because
- 2:56sub-texel games
- 2:57>> [music]
- 2:57>> do not have to worry about the issues
- 2:59mentioned previously.
- 3:01Let's have a look at the magnitude
- 3:02[music] of the problem actually in our
- 3:04engine.
- 3:06We can see here a typical example of the
- 3:08type of sprite we might see in a pixel
- 3:10art game.
- 3:12>> [music]
- 3:12>> When we start to move this sprite across
- 3:14our screen, the issue is immediately
- 3:16obvious. We've lost the coherency of the
- 3:19original sprite, and we see this shimmer
- 3:21that leads to an unstable image.
- 3:24And this is due to pixels [music] not
- 3:26uniformly crossing the grid boundaries,
- 3:28which results in the squash and stretch
- 3:30[music] effect we see in this demo. To
- 3:32mitigate these floating point issues,
- 3:34>> [music]
- 3:35>> we can look at locking the camera to the
- 3:36pixel grid.
- 3:42Implementing this is a relatively
- 3:44straightforward task. [music] If we can
- 3:46calculate the size of a single pixel in
- 3:48the high resolution output, we can then
- 3:51use that information to ensure the
- 3:53camera is always positioned at a
- 3:55multiple of that value using a rounding
- 3:58function. [music]
- 4:01Now, this is good news for our horse
- 4:03callus. [music] With the grid snapping
- 4:05implemented, we're able to move around
- 4:07the world and explore [music] the space
- 4:09without the extreme shimmer we saw in
- 4:10the demo.
- 4:13>> [music]
- 4:14>> When we enable the same grid snapping as
- 4:16we saw in our main scene, we can witness
- 4:18the issue more clearly. We see that the
- 4:21shimmer no longer exists, [music] but
- 4:23now the camera is restricted to move in
- 4:25discrete blocky steps. And the lower the
- 4:28resolution, the worse this becomes.
- 4:32Our camera no longer has the ability to
- 4:34move smoothly. [music]
- 4:35This blocky motion really doesn't feel
- 4:37great to play, and we'll need to come up
- 4:40with a way to have a grid aligned camera
- 4:42whilst maintaining [music] sub-texel
- 4:44motion.
- 4:47The first part of solving any problem is
- 4:50identifying the source of the issue.
- 4:52>> [music]
- 4:52>> When we rounded the camera position to
- 4:54the grid, we introduced a discrepancy
- 4:56between where the camera is and where
- 4:58the camera renders. We can capture this
- 5:00loss by storing the difference between
- 5:02the position and its rounded location
- 5:05into a new variable.
- 5:11Originally, we identified that we
- 5:12couldn't move the camera smoothly
- 5:14because of this high-to-low resolution
- 5:16mismatch.
- 5:17>> [music]
- 5:18>> But surely, if the movement was computed
- 5:20after upscaling, high-to-high resolution
- 5:23should not display any of these issues.
- 5:26>> [music]
- 5:29>> So, let's create a new render texture
- 5:30and copy over the result of our scene
- 5:33camera into this dedicated RT.
- 5:37We'll establish now a secondary game
- 5:39camera alongside the one currently
- 5:41rendering our scene. And this new camera
- 5:44has one job, and that's to render out
- 5:46this new texture created from our first
- 5:48pipeline.
- 5:50The scope of this new camera is just a
- 5:52raw image, which sits perpendicular to
- 5:54the view direction. [music]
- 5:56We disable any additional render passes
- 5:58or post-processing, as this is handled
- 6:01when the scene is rendered by the main
- 6:03camera.
- 6:05Now, reintroducing the stored loss
- 6:07variable from before,
- 6:09>> [music]
- 6:09>> we can offset the transform of this RT
- 6:12plane by the vector calculated in the
- 6:14rounding step. And so, every frame our
- 6:17texture represents [music] the position
- 6:18of our render camera minus the
- 6:20discrepancy lost when rounding.
- 6:23And this might be a little difficult to
- 6:25conceptualize, but essentially our view
- 6:27of the scene is now able to move
- 6:29smoothly anywhere in the world,
- 6:31>> [music]
- 6:31>> and at the same time the render camera
- 6:33can only be positioned on discrete grid
- 6:36boundaries.
- 6:42Now, Callus is completely free. We can
- 6:45move around the world unimpeded by
- 6:47jitter or snapping.
- 6:48>> [music]
- 6:50>> Whilst the image isn't entirely
- 6:51shimmer-free, as not all surfaces are
- 6:53perpendicular with the camera, compared
- 6:55[music] to the old method, the game feel
- 6:58is dramatically improved.
- 7:00>> [music]
- 7:02>> Our camera system is now able to
- 7:04smoothly handle translations and
- 7:06movement. But potentially, we don't want
- 7:08to be limited to just a single camera
- 7:10angle when making a game. So, the last
- 7:13thing I want to look at today is
- 7:14handling rotation
- 7:16>> [music]
- 7:16>> and how we might orbit the camera around
- 7:18a central object.
- 7:23From my current understanding, rotation
- 7:25is somewhat of an unsolvable problem.
- 7:27The issues we mentioned earlier of pixel
- 7:30crawl across grid boundaries cannot be
- 7:32avoided when dealing with rotation.
- 7:37A fairly intuitive way to see this is to
- 7:39imagine the camera from the pixel's
- 7:41point of view.
- 7:42>> [music]
- 7:43>> When the camera rotates, we can imagine
- 7:45the pixel as a projection onto the
- 7:47surface of a sphere.
- 7:52The position of that pixel on the sphere
- 7:55is some cosine of the view vector.
- 7:58And from there, you can see that for a
- 8:00screen of pixels, [music] not all of
- 8:02these values can live at fixed grid
- 8:04intervals.
- 8:07If we try to deploy our same tricks from
- 8:09earlier, translating high to high
- 8:11resolution, we would first need a
- 8:13spherical or cube-based map of the scene
- 8:16from multiple angles.
- 8:17>> [music]
- 8:18>> And my intuition feels we couldn't get
- 8:19away with potentially six additional
- 8:22cameras without compromising on the
- 8:24vision of a game.
- 8:27So, there's no way around it. We have to
- 8:29bite the bullet and implement a standard
- 8:31camera rotation, knowing that jitter
- 8:33will occur.
- 8:34>> [music]
- 8:35>> As we've seen previously, a rotation
- 8:37matrix or comparatively an inbuilt
- 8:40[music] function call can implement this
- 8:42trivially.
- 8:44Depending on the compression of this
- 8:46video, you'll see the results are
- 8:48somewhat polarizing.
- 8:50I'm keen to hear what people think about
- 8:52rotation in general.
- 8:53On one side, you might feel it's part of
- 8:55the [music] charm, and once you play for
- 8:57a while, you don't notice it.
- 8:59And others may feel that the jitter is
- 9:01too distracting to the presentation.
- 9:03And this potentially throws into
- 9:05question the type of game you might want
- 9:07to make with this tech.
- 9:10Similar to our approach in the God Ray
- 9:12episode, we have a few non-pixel
- 9:15techniques that we can look at to remedy
- 9:17this, but at the expense of breaking the
- 9:19faithfulness of the aesthetic.
- 9:22Blur is perhaps a controversial topic,
- 9:25but by applying a small amount to
- 9:27average out neighboring pixels, we can
- 9:29mask some of the artifacts. But the
- 9:31smudgy look that this adds is quite the
- 9:34departure from our original crisp image.
- 9:37A different attempt at solving this is
- 9:39to apply a global dither across the
- 9:41entire screen. The idea being that our
- 9:44brains have a harder time noticing pixel
- 9:46crawl in a sea of different values.
- 9:49This is a bit more radical, but it seems
- 9:51to be effective if [music] we can
- 9:52tolerate the slight washed out look it
- 9:54gives when rotating.
- 9:57And the last technique is to steer into
- 10:00the jitter and make it a part of the
- 10:01style. Since the resolution in our new
- 10:04camera system is just a parameter, we
- 10:06can increase the pixelation amount when
- 10:08rotating the screen to alias areas of
- 10:11jitter.
- 10:12This, in addition to other effects, can
- 10:14really create a stylized look where it
- 10:17may be easier to get away with
- 10:18instability if the tone of the game
- 10:21matches the virtual look.
- 10:24These three techniques don't at all feel
- 10:26like solutions to our issue, but in
- 10:27general, it does highlight that we can
- 10:29sometimes improve the results of a
- 10:31technique by finding ways to remedy
- 10:33their weaknesses.
- 10:38And that concludes the content for this
- 10:40episode. We've looked at dealing with
- 10:43our original low-to-high resolution
- 10:45problem, how we can move the camera
- 10:47smoothly aligned with a grid, and ways
- 10:50we might try to deal with rotational
- 10:51[music] artifacts.
- 10:54Let me know what you think about the
- 10:55results in the comments, and I'm curious
- 10:57[music] to hear ideas on how we might
- 10:59improve the result.
- 11:01Thank you so much for all the support
- 11:03over on Patreon. [music]
- 11:04And if you made it this far in the
- 11:05video, a like and subscribe goes a long
- 11:08way to help out the channel.
- 11:10>> [music]
- 11:12>> As always, thanks for watching.
- 11:39>> Mhm.
About this transcript
This page contains the full transcript of How I solved my biggest pixel art problem | Pixel Perfect by Red Giraffe, generated from the public captions YouTube serves with the video. The transcript has 1,776 words across 307 segments, with the original timestamps preserved so you can click any line to jump to that moment in the embedded player.
What you can do with it
Use the transcript to take notes, quote the speaker, build a study guide, generate a summary with ChatGPT or Claude via the YouTube Summary tool, or export it as a timed subtitle file with YouTube to SRT. You can also re-open it in the transcriber to translate the transcript into 100+ languages.
Free YouTube transcript tool
YouTube2Text is a free YouTube transcript generator — no signup, no daily limit. Paste any YouTube link and get the full transcript instantly, with timestamps, click-to-jump, translation to 100+ languages, AI prompts for ChatGPT, Claude, and Gemini, and exports to TXT, SRT, VTT, or Markdown.