Drawing the Line on 3D Pixel Art | Pixel Perfect

Full Transcript — Download SRT & Markdown

00:01
Speaker A
When we try to bring pixel art principles into 3D, we run into problems.
00:05
Speaker A
Standard rendering techniques add too much detail.
00:09
Speaker A
Anti-aliasing smooths edges that should stay sharp.
00:13
Speaker A
Lighting creates gradients where we want clear color separation.
00:18
Speaker A
In the last video, we looked at how to form our 3D scene into a stable, low-resolution grid and map its colors to a perceptual space.
00:27
Speaker A
But even with a perfect grid, we're still fighting some of the knock-on effects of working in 3D.
00:33
Speaker A
The problem I want to look at today is introducing more structure to the image.
00:40
Speaker A
Outlines are one of the most powerful tools pixel artists use to define form and create visual clarity.
00:46
Speaker A
In traditional pixel art, outlines serve multiple purposes: they separate objects from background, compensate for limited resolution, and they give the artist direct control over which edges get emphasized.
00:59
Speaker A
To give our pixel art render the same artistic treatment, we'll need to outline objects in our 3D scene.
01:50
Speaker A
Before we start, I greatly appreciate the reception to the last video.
01:54
Speaker A
A lot of insightful feedback and questions in the comment section, so thank you for that.
02:00
Speaker A
If you are new here, subscribing is the best way to help support the channel and keep up to date with the series.
02:05
Speaker A
Thank you, and let's get into it.
02:18
Speaker A
The most intuitive way to find an edge is to look at areas of change.
02:22
Speaker A
If pixel A is green and pixel B is orange, there might be a boundary located in this area. We could use a filter to scan the image and highlight these areas of change in the hopes that it would return edges.
02:37
Speaker A
The problem is that color is a dirty signal. A shadow falling across a flat wall creates a color shift, which can mislead our filter into placing an edge where none belongs.
03:29
Speaker A
If we base our outlines on color alone, the engine can't distinguish between the silhouette of a character and the wood grain on a table. Furthermore, standard post-process outlines create mixels, an image made of varying sized pixels.
03:46
Speaker A
To fix this, we'll need to construct our outlines using a signal, which is much more stable than color.
03:54
Speaker A
Before a single ray of light is calculated, the engine has to solve the problem of occlusion. It needs to know which surfaces are in front of others.
04:03
Speaker A
This process produces the G buffer, a series of auxiliary textures that serve as the mathematical scaffolding for our scene.
04:15
Speaker A
During rasterization, the distance of every fragment from the camera is recorded into a high precision texture. By comparing the depth of neighboring pixels, we can isolate discontinuities. A sharp jump in value marks the precise boundary where the silhouette of one object overlaps another.
05:19
Speaker A
The normal buffer stores the surface vectors. This is how we find internal creases. Even if two parts of the same object are at the same depth, if their normals are pointing in different directions, like the corner of a building, we have a structural edge.
05:39
Speaker A
Using these techniques in tandem, we can combine both our detected silhouette and crease edges into one outline texture, which can be composited back onto our final render.
05:50
Speaker A
Using the same downsample render pass that we wrote in the previous video, we can easily pass both of these generated depth and normal textures to have the same treatment as our color render.
05:59
Speaker A
This allows all three of the textures to conform to the same pixel grid and ensures all pixels placed align with the objects in the scene.
06:52
Speaker A
Most GPUs store depth non-linearly. Because in conventional rendering, we need more precision for objects close to the lens, the depth values are logarithmic.
07:01
Speaker A
If we use this raw data, an edge threshold that works for a nearby object will completely fail for an object in the distance. We use a linear eye depth transformation to convert these values back into world space units. This ensures our edge depth bias remains consistent. A small distance in the geometry will trigger the same outline, whether it's 5 meters or 50 meters away from the camera.
07:31
Speaker A
But even with perfect data, we have to ensure the marks respect our low-resolution pixel grid.
07:39
Speaker A
We do this by calculating the exact size of a single texel based on our target pixel resolution. Instead of just checking a single coordinate, the fragment shader performs a proximity search.
08:25
Speaker A
It checks the immediate neighbors, up, down, left, right, to see if any of them are inside the object's mask. This reduces the chance of double placing pixels in geometry that is at a skew to the camera's view direction, and in most cases, it ensures that outlines we place are exactly one pixel thin, regardless of what we choose for our resolution.
08:55
Speaker A
Let's look at implementing some of this work in engine. By using this scene of primitive shapes, we can test our pipeline pass in a stripped-back environment before moving to a more complex scene.
09:07
Speaker A
Using a debug script, we can isolate both our depth and normal textures and render them straight to the screen. We can see that these representations of the scene are both healthy and give us the data we will need to outline the objects.
10:04
Speaker A
Looking at the code for implementing this, again, using a separate render pass, we can see that it's made up of two simple functions for checking the values of a pixel plus an offset and then return whether the difference between these pixels meets a given threshold. Another important detail here is that we've built in a priority for the outline types. We can see here in the crease function that if a pixel is already accepted as a silhouette, we yield its ability to then also become a crease. And this is done out of no other reason than a stylistic choice. Resolving the conflict here feels more in line with what an artist might choose.
10:45
Speaker A
Now applying this to the shapes and using the same debug script as previous to isolate this texture to the view, we can see our outlines pop into existence. With silhouette edges denoted in blue and the creases in red, our outlines hold up pretty well. They align exactly with our low-resolution grid, are typically one pixel thin, and display a fair resilience to jitter. There are a couple of pixel discrepancies that cause a slight amount of popping, but this is somewhat unavoidable when the geometry is skewed. And although when seen in a shaded scene, the effect is quite minimal, having select outlines on the right objects in an environment can do a lot for the readability, especially in a limited color space.
12:14
Speaker A
Now moving to a more complex scene and applying our outline techniques uniformly, we can see how our work holds up with a denser area of objects.
12:24
Speaker A
Immediately, we can see the issue. Whilst the outlines do look crisp and in some places help highlight key objects, applying outlines to everything in the scene destroys the readability.
12:36
Speaker A
Meshes, in particularly, with a high density of triangles, create a lot of crease edges. And this is in part a fault in the asset choice, but in the future, we'll want the freedom to use a variety of assets. And so this calls for the need to be selective with which objects receive these highlights.
13:37
Speaker A
We can use a channel encoded mask. All objects in the correct layers are rendered into a dedicated texture using specific layer masks. We then use the color channels to tell the shader exactly which algorithm to apply. And this selective mask can then be composited back onto our final render.
13:56
Speaker A
Implementing this involves a separate render feature to build the mask. When we render an object in the scene, and if that object has the outline layer mask, we write an additional set of pixels to a new texture at the location of that object. And this builds a supplementary texture that tells us where all of the tagged objects exist.
14:16
Speaker A
The most important detail here is that this process needs to respect occlusion. We feed our depth texture as a secondary input so that only the objects that are in view of the camera get written to the mask. Otherwise, we can discard this pixel.
15:12
Speaker A
From here, we have an algorithm that builds pixel perfect outlines and a mask that tells this process where on the screen it can operate.
15:23
Speaker A
Looking at this composited on the final render, we can see now that we have much more clarity and the overall image strikes a good balance between being its own visual style, whilst reminiscent of hand-drawn pixel art.
15:37
Speaker A
It's important to note that whilst these outlines are subtle, in a game context, these small visual indicators can help communicate to a player, perhaps what is interactable or where the boundary of a solid object lies.
15:54
Speaker A
And that concludes what I wanted to discuss today about outlines. While this isn't strictly a tutorial, I hope it shed some light on my thought process and ideas around how I go about building things. Anyone watching this video will be aware that there's a fair amount of 3D pixel art out there, and I'm merely resting on the shoulders of giants.
16:52
Speaker A
And I do want to add that I'm not trying to create a copycat style, but make something that comes from my own artistic interpretation of what 3D pixel art means. Let me know in the comments what you'd like to see more of, whether that's more code or demo scenes, diagrams or failed experiments.
17:13
Speaker A
There's a lot that goes on behind the scenes before we see the final render, so if you did make it this far in the video and you enjoyed the content, I'd really appreciate a like and subscribe.
17:25
Speaker A
It really does help, and as always, thanks for watching.

Get More with the Söz AI App

Transcribe recordings, audio files, and YouTube videos — with AI summaries, speaker detection, and unlimited transcriptions.

Or transcribe another YouTube video here →