Investi­gat­ing a Rocket League Render­ing Bug

Photo of Dillon in front of green leafy background wearing a colorful mask. Photo of Dillon in front of green leafy background wearing a colorful mask.

I’ve got to hand it to Psyonix, they’ve managed to hook me with Rocket League more than any other game in a long while. Rocket League looks gorgeous running on a decent PC, yet still runs perfectly fine on my laptop with integrated graphics. The fact that I can play the game practically anywhere in quick bursts has made the game infinitely more replayable. It looking good when I’m on a proper PC is just a bonus.

However, much like with any game, with enough time you’ll come across little graphical quirks that haven’t been accounted for, or are hard to work around. Often times you won’t ever notice the issue unless you do very specific sets of actions, such as with this interesting bug.

Screenshot from Rocket League. The ball is in the foreground but rendering behind a decorative ball in the distant background as part of the environment.
It's like the ball has a little baby ball

The game ball, which is much closer to the camera, is being rendered behind the prop ball that’s part of this arena’s background. This is not the only part of this map that has this rendering issue, in fact there are a number of background elements that have the same bug.

Same bug as shown by the previous image, but with a different part of the map's set dressing.

Same bug as shown by the previous image, but with a different part of the map's set dressing.

Same bug as shown by the previous image, but with a different part of the map's set dressing.

I was only able to find examples of the bug on replays for Neo Tokyo, and it isn’t just a case of that map being broken, since normally you’d see the ball rendered correctly.

A recreation of the first screenshot of the two balls, but with the correct render order where the two balls overlap.

The bug only happens when you pause the replay, and skip forwards or backwards in time. So what’s something that only happens after skipping time, that doesn’t happen when paused?

Still a better materialize than the TARDIS

After skipping time, the ball plays a forming animation. It is only once that is complete that it starts reflecting the arena, and casts a shadow. So now we know the bug is with this pre-formed version of the ball, or at least that it’s relevant.

Now, it’s important to note that it’s not exactly strange for Rocket League to render things in an order different to their actual positions in the world, such as with this goal animation…

This reaper goal animation is unique, and it’s not because it’s the only one I cringe at everytime I see it. Unlike every other scoring animation (that I can tell), this one renders on top of everything, and actually goes below the floor.

This isn't what I meant by 'flooring it'

The way it is rendered means it also appears in front of player vehicles, in a similar fashion to the issues we see with the ball on Neo Tokyo.

He's still yet to win a game of hide & seek

In this instance, it’s to be able to exaggerate the size and motion of the animation, without worrying about it sinking below the ground — the cars failing to show in front is merely a side-effect — but this does show that the Rocket League developers don’t necessarily render everything in order.

So by pulling up a graphics analyzer tool (I’ll be using Intel GPA), we can start to have a look at where it goes wrong in Neo Tokyo.

I’ve captured this frame of the bug using Intel GPA and extracted the different passes that go into making a frame like this.

Another screenshot of the bug where a background set dressing ball is rendered in front of a foreground game ball.

The first major pass includes most of the geometry in the scene, and lacks a lot of shading and effects. Let’s call this the main pass.

The same screenshot as before, but showing the first pass of it's rendering. Both balls, the floor, the glass walls, the atmosphere and lighting effects are all missing.
Main Pass - Bugged

The second major pass has shading effects, some additional background graphics, clouds and the ball. Let’s call this the detail pass. This pass has transparency, and renders on top of the main pass.

The next pass of the rendering for the same screenshot. This one only contains one of the balls, but also includes atmospherics, the glass walls, the floor, and the lighting effects.
Detail Pass - Bugged

Now let’s have a look at the main pass and detail pass for a frame where the ball is shown correctly, like the one we saw earlier.

A screenshot showing the first pass of rendering for a frame without the bug. Unlike the bugged example, this frame shows the game ball.
Main Pass - Normal
A screenshot showing the second pass of rendering for a frame without the bug. Unlike the bugged example, this frame does not contain a ball in it.
Detail Pass - Normal

And we can immediately see a difference: the ball is showing up in the main pass, and not the detail pass in the correct version, which tells us the problem involves something that differs between these passes.

A 4x4 grid reshowing the same screenshots, for more direct comparison.

There are two differences, and both are related to each other. The detail pass is transparent, and does not affect the depth buffer.

In order to produce certain effects, games keep what is called a depth buffer: an image that stores the distance a given pixel is from the camera. This might be used to create a depth of field effect, by blurring parts of the frame differently based on their distance from the camera.

Another use for these depth buffers is depth testing, where you use that information to tell if one object is in front of another. The Reaper and the game world have their own depth buffers, so combining them and some passes with a small Python script, we can see what The Reaper would look like with depth testing.

Screenshot of the reaper goal animation, when the model is part way under the floor. The screenshot shows that the reaper renders in front of the floor despite this intersection. The same screenshot, but where the intersection with the floor is rendered correctly, with part of the reaper being occluded.
Top: Original, Bottom: Modified

Transparent objects can’t and shouldn’t affect this buffer: After all, if you have a window that affects this buffer in front of a players view, the game would think that faraway objects seen through it are right in front of the camera.

Here’s the visual horror that is the depth buffer for the correctly rendered frame.

The raw depth buffer for the non-bugged frame. It has bands of purple, green and light blue covering the entire environment.

At the cost of any semblance of accuracy, I’ve gone ahead and adjusted it to be easier to look at.

The same depth buffer converted so that white is used for pixels far from the camera, and black for pixels close to the camera.

The brighter the pixel, the further away from the camera it is. Since the ball is dark, it is relatively close to the camera compared to the background. So when a depth test is performed for the background elements, it will correctly determine that there’s a ball in front of it for those pixels.

And here’s the depth buffer (also adjusted) for the bugged version.

The depth buffer for a bugged frame, which has been converted for white meaning far from the camera, and black meaning close.

When the depth test is performed on this version, it has no way of knowing there’s a ball between it and the camera, and so ends up being drawn on top.1

The only reason the depth test causes a problem here, and not with other objects in the scene is that these specific elements are being rendered after the detail pass, the one containing the ball in the bugged version. Since the detail pass ends up on top of most of the arena’s geometry, it can mostly go unnoticed.

While I can only speculate, I believe it’s done this way to prevent the atmospheric effects or clouds from obstructing these background elements, given that they’re supposed to be bright and stick out. The side-effect just happens to be that they’ll render in front of anything that doesn’t affect the depth buffer.

I’ve also had it suggested that they need to be rendered specially because they’re the parts of the background that are animated, which is also entirely possible.

Regardless of the why, since we know how this happens, it makes it very easy to find other instances where this bug will occur: whenever transparency is involved. Like, say, the transparent goalposts from the Autumn Update…

A screenshot of the first pass of rendering, that shows that the inside walls of a goal aren't rendered as part of the first part
Main pass image of inside a goal

And as to be expected, it does in fact have this rendering bug.

A screenshot of a final frame where a set dressing element in the background is rendered in front of a goal box's walls, despite the camera being placed inside the goal.

So what is the lesson we learn from this? Bugs like this are neat to deconstruct, but they do show that the way we render things can and will have side-effects. Most of the time they’re invisible, only showing themselves in contrived situations, or they’re so invisible in the moment-to-moment game that we fail to notice them.

In this instance, Psyonix could probably fix the issue with the ball by using the version that’s part of the world proper right up until the materialize animation actually starts. But the transparency issue as a whole would still exist, it would just be less noticeable; then we wouldn’t have been able to deconstruct why this happens.

That’s kinda the beauty of rendering bugs. They teach us a little about how everything works under the hood2, or at least get a sense of the compromises that allow for something to work as efficiently as possible.

Now, originally that was going to be the end of the post. After all, the culprit of the bug has been discovered, and it’s been tied up neatly into a point about side-effects and discovering the inner workings of a system.

But what happened here is I actually fell into a bit of a trap. I was so invested in my search, that I didn’t step back a little and ask a very simple question.

Does this bug still happen if you change graphical settings?

Only once I had a support ticket open with Psyonix, and the support person (thanks Robin!) asked if I’d messed with any of my graphical options, did I consider checking to see if it made any difference.

It did.

Turning off High Quality Shaders

So here’s a quick lesson 2 for this post. Remember to look broader. Don’t think that just because you’ve found your culprit, that you’re done searching. Maybe it only happens on certain configurations, or you haven’t considered all the conditions required for something to happen.

By not doing that simple check, I failed to make note of an important condition for this bug to happen: that High Quality Shaders must be turned on. And if I was a developer on a game like this, there might have been a few false starts on trying to submit a fix.

That being said, the bug still happens with one background element when High Quality Shaders are off…

One element of the set dressing is shown to still exhibit the same bug with the foreground ball, despite the high quality shaders setting being off.

And so, as there are more elements at play than I could ever be able to uncover as an outside observer, I must throw in the towel here.

Thanks for reading.

  1. This actually causes another slight bug that you can see in the early screenshots: parts of the ball end up blurred while others are in focus, since it’s using the depth information of the background when performing the depth of field effect. 

  2. Car pun not intended.