Dev Blog 31 - December

Recently, I’ve been learning Chess. NO, it’s NOT because I watched Queen’s Gambit.
It’s because my friend watched Queen’s Gambit and asked me to start playing with her.

We have a lot of house rules.

By the way, go watch Queen’s Gambit.

Let’s get into the update!

I wrapped up the goalposts for the final two worlds which still lacked one.

You’re going to have to be a little more careful when you reach the goal on some of these stages.

I’m sure you’re excited to see some more of our late-game levels! I’ve been hush-hush on the harder stuff, but it’s been a while - we can afford to share a little peek.

Also, as per usual, we have a writeup from the most wonderful CraftedCart. Take it away!

Still on that bugfix train

Thinking of stuff to write about for these dev updates is really tricky when I’ve just been doing boring ‘ol bug fixing. >.< Hmmmmm…. oh, here’s a fun one from a little while back!

Minimap transparency

The minimap! It’s the map… that’s mini!


You’ll have to excuse the mismatched background here, the latest Nvidia Linux drivers have been uhh… well you don’t want to see what this looks like in the ice background, and I’ve just been too lazy to downgrade them again.

Now, you may notice one thing about that minimap… it doesn’t show the bottom of the stage. You may have seen sometimes in games or 3D rendering that there’s a cut-off point in the distance which you can’t see anything beyond - the “far clip plane” it’s called. There’s also a “near clip plane” where you can, well, see through objects if you get too close to them. The reason for having these clipping planes is that we only have a limited amount of bits to represent numbers to represent depth between the near and far plane - a smaller range between the near and far plane gives us more precision, whereas a farther range lets us see further/closer.

So naturally, my first thought was maybe the camera that was rendering the minimap had a far clipping plane that was too close. As it turns out however, that plane isn’t adjustable, and is instead fixed at infinity (however that works)!

…a little bit of poking around later, I figured I’d take a look at how the minimap is even rendered to.

Minimap capture settings

So.. just a brief primer on image channels if you’re not aware: an image can have multiple color channels - usually you’ll have an R/G/B channel to hold the red/green/blue value of each pixel, sometimes you’ll also have an A channel for alpha (opacity). In this case, we have 4 channels to play with here (RGBA) and if you look at “Capture Source”, the minimap is set to render RGB color into the, well, RGB channels, and the depth of each pixel into the A channel (how far away from the camera each pixel is). So that’s interesting I guess…

Next stop: let’s take a looksie at how the minimap material actually works (I’ve added in a fake transparent checkerboard in the background to help visualize how this looks).

The original minimap material

So, the gist of what’s going on here is the RGB color of each pixel is just identical to the RGB color of each pixel on the rendered minimap texture. Each pixel can also either be visible or hidden, depending on the “opacity mask”. Here, we divide the depth of each pixel by 24000, throw away all pixels that are further away than 24000 units, then throw away all pixels that are outside of the white circle texture at the bottom (since the minimap is circle-shaped in the UI).

So if the cutoff point is 24000….magical-units, then let’s try increasing it! Why not 99999 magical-units?

...with a cutoff of 99999

…ah, that’s why. Didn’t exactly want a black circle around it.

So, at this point I started playing around to see how far I could raise the cutoff point, before a black circle appeared around everything. Skipping past all that however, I eventually found out that the minimap texture was stored with a format of RGBA16f. What this means is that there’s the afformentioned RGBA channels, but also that each pixel in each channel is stored as a 16-bit floating point number - and with this info, I can find out what the maximum possible value for the magical-units are (65504).

So, let’s subtract one for safety, and divide by 65503 instead, aaand…

...with a cutoff of 65503


…wait what do you mean Unreal can just render to a texture with opacity, instead of needing to do this depth workaround thing…


aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [jumpcut]

Anyways, with that out of the way, there was still one more issue: additive materials (the slightly glow-y ones found everywhere in the VR world) don’t show up… at all.

Minimap's looking interesting huh

So, additive materials don’t write to the “depth buffer” (a texture that keeps track of how deep each pixel is), so we can’t see them in the alpha/depth channel in our render target. In the end, I just decided any pixel that wasn’t completely black should be visible, even if the depth value was out-of-range.

There we go

Perhaps not the prettiest (given additive materials show up as opaque-against-black now on the minimap), but waay better than before. This’ll do just fine!

operation of a boat

We’re going rowing.

See you all in the new year!