A shader for spotlights

December 16, 2020
A shader for spotlights

When it comes to graphics and aesthetics in a game I think they should both move over to let readability come forward. This is the reason why explosive barrels are red, no doubt about it.

However, The Nexatli Expedition does not have the luxury of color, and I still needed a way to highlight important parts of a room. The brightness of the tiles and their shape is usually enough to convey to the player where he can move and where he should jump, but sometimes I wanted an extra focus on some actors, triggers and doors. In the demo, this can be seen near Annah in the hangar and more prominently in the basement, where it looks like there's a spotlight pointing at stuff you should notice when playing.

The secret passage obscuring tilemap

That's why I created a simple actor that I could place and customize the look of with a dynamic material. I called it decal, although it's not really a decal in UE4 terms. The actor handles a small plane which can be resized with widgets, and the material uses the scene color to make it lighter or darker. The process is similar to the one used to shift the color of the sprites as I explained in a previous blog post.

The secret passage obscuring tilemap

As you can see, the material is a really simple, translucent and unlit shader. Most of the nodes are linked to params that allow me to change the look of the decal, you know the drill by now. Let's see it in detail:

The secret passage obscuring tilemap

More than half of the material is comprised of a RadialGradientExponential node with its parameters. This creates a radial gradient from black to white centered where I want, within the aforementioned plane. The UVs for the gradient have been tweaked to make sure they keep the discrete look and don't actually look smooth like a real gradient should be. They're not really noticeable in the preview, but they're there.

The secret passage obscuring tilemap

The next step involves a small 4-by-4 texture. You can make this texture larger, but it's important that it has a "grainy feel", with color ranging from black to white. The dithering part of the shader defines how gritty the gradient should be around the edges, pushing the color of the gradient away from grey and near black or white. The "DecalGrittiness" parameter is set to 1, but 0.5 and lower could be better values, especially if you're working with more colours. I zoomed in the preview so you can notice the resulting texture, mapped onto the plane.

The secret passage obscuring tilemap

Combining the gradient with the dithering and rounding the values outputs a strong opacity mask that will be used to mask the overall effect.

The secret passage obscuring tilemap

Similarly to the sprites example I mentioned earlier, the scene color is shifted up or down in brightness according to a parameter and later locked to my usual 4 colours using the scene color as UV on a striped texture containing my palette. You should disregard this step if you're not working with a set palette and you feel like experimenting. You could use the mask with a simple yellow tint or animate the shift value over your palette to simulate the spotlight warming up.

The secret passage obscuring tilemap