Game
Tales of Kulplex
6 years ago

Tales of Kulplex, Devlog #4 - Powering Up Sprites with Shaders


Hello!

In this devlog, I thought I would go over something which took quite a bit of time: extending what Unity sprites can do.
Tales of Kulplex is technically a 2.5D game, but the term “2.5D” has quite an imprecise definition. It is loosely defined as: “between 2D and 3D”.

That could mean 2D graphics for characters and 3D objects for the environment:

5d09cdee8fb0c.jpg

Paper Mario


It could also mean the complete opposite (3D graphics for characters and 2D for the rest):

5d09cdefd0a6d.jpg

New Super Mario Bros. U


Or 3D objects restricted to 2D movements:

5d09cdf123812.jpg

Donkey Kong Country Returns


Or even just a game with 2D graphics but with 3D movement:

5d09cdf1d1560.jpg

Castle Crashers


Tales of Kulplex falls into the last category. For this reason, I think “simple” sprites, while perfectly suited for pure 2D games, wouldn’t be sufficient to bring out the best of the additional half dimension (the remaining 0.5D).

What Unity provides

Sprite is a generic term for 2D images (for some nice additional info: Sprites on Wikipedia) and are used pretty much
in every game, from menus and UI to just about everything in the game.
Unity, having a 2D engine as well 3D, clearly supports sprites and provides several different shaders to modify the rendering of sprites.
Here is an (incomplete) list of shaders which can be used for sprites, with an overview of the features I care about:

Sprites > Default:

		
			- Lighting: NO
- Tint: YES
- Sprite Transparency: YES
- Runtime Transparency: YES
- Cast/Receive Shadows: NO
		
	

Sprites > Diffuse:

		
			- Lighting: YES
- Tint: YES
- Sprite Transparency: YES
- Runtime Transparency: YES
- Cast/Receive Shadows: NO
		
	

UI > Default:

		
			- Lighting: NO
- Tint: YES
- Sprite Transparency: YES
- Runtime Transparency: YES
- Cast/Receive Shadows: NO
		
	

UI > Lit > ALL:

		
			- Lighting: YES
- Tint: YES
- Sprite Transparency: YES
- Runtime Transparency: YES
- Cast/Receive Shadows: NO
		
	

UI > Unlit > ALL:

		
			- Lighting: NO
- Tint: YES
- Sprite Transparency: YES
- Runtime Transparency: YES
- Cast/Receive Shadows: NO
		
	

Unlit > Transparent:

		
			- Lighting: NO
- Tint: YES
- Sprite Transparency: YES
- Runtime Transparency: NO
- Cast/Receive Shadows: NO
		
	

And an additional shader, normally for 3D objects, which doesn’t support transparency within the image:

Standard:

		
			- Lighting: YES
- Tint: NO
- Sprite Transparency: NO
- Runtime Transparency: NO
- Cast/Receive Shadows: YES
		
	

Something which quickly stands out is the fact that not all the default sprite shaders support shadows and lighting may or may not be supported.

Question: What should one do if they want to support: Lighting, Tint, Transparency AND Shadows?
Answer: Make a custom shader! (SPOILER: It turns out supporting runtime transparency AND shadows at the same is a pain, so sadly I had to compromise.)

Pimping up the Sprites

If we just took the Default Sprite shader and threw in a couple of sprites and a cube, you’d have something like this:

5d09cdf2c9dbf.png

It looks fine but definitely a little flat, as there is nothing to give the feeling of depth.

Shadows

The first important step would be to support shadows, that is possible with a neat shadow shader (Anlev’s Sprite Shader on GitHub):

5d09cdf3e6280.png

That already looks much better! Now both 2D and 3D objects can receive and cast shadows on each other, giving much more depth.

Bonus: Normals for Sprites? No way!

While exploring what sprites normally aren’t capable of, I found out that it’s possible to add normals to sprites. According to Wikipedia, normals (or normal mapping) is a technique used for faking the lighting of bumps and dents.
It uses a map of the desired bumps in the sprite to give a more natural appearance.
To create and apply a normal map, one can use any image editor and do it manually, or use something like TexturePacker (it’s not completely for free but some features are).

5d09cdf508151.png

And even with a very badly generated normal map (I know, it’s really bad), the background looks a bit more “3D-ish” and totally badass.

Shadows AND Transparency

It turns out that using the shadow shader (using a surface shader and a Lambert lighting model), the transparency becomes a cutout:
either fully visible or fully invisible.

5d09cdf635420.png

That’s no good when you would like to fade in or out sprites to show/hide certain locations at runtime.
To support both shadows and transparency, 2 additional passes are required (one for the transparency and one to bring back the depth, lost in the previous step), making it unnecessarily heavy for just rendering simple sprites.

After lots of iterations and tests, I realized that I really never needed them at the same time, so I compromised to use 2 shaders instead:

No Transparency / Shadows

5d09cdf6ce3b7.png

Transparency / No Shadows

5d09cdf7e2e30.png

Bonus: Transparency AND Normals? Madness!!!

What about those normal maps we briefly talked about earlier? Well, they work just as well with transparency, so that’s great news!

5d09cdf8dd2a4.png

Conclusion

After spending a long time studying, researching and discussing with other developers, I ended up with a non-elegant solution (supporting everything with 2 shaders rather than just 1). The developer in me was at first a bit upset that I didn’t manage to achieve both tasks at once: supporting transparency and shadows doesn’t sound SO complex.
It was actually after more studying that I fully understood (finally) how transparency, lighting and shadows are calculated and applied.

Even major AAA companies need to be careful when handling transparent objects in their games, either they can look bad or perform poorly (a very interesting presentation about lighting in Far Cry 5 here).

In the end, I learnt that sometimes compromising is not so bad and can be the best way forward.

Here is a little compilation of the different tests:

5d09cdfa4a1f0.gif

You can get in touch with us and stay up-to-date on:

Feel free to leave a comment, especially if you have something to add or fix! :D

Take care!

Nesh



1 comment

Loading...

Next up

We are under attack!

Path of Kami: The Evolution of the Lore

I was bored, so... ManutKat.

a drawing I did because I was bored😪. But I love how it turned out.😊

not the greatest house, but it's a house! :D

2d lighting experiment

I did this on my 3DS ^^ #Kirby

Our menu, gathered a huge feedback and positive comments among foreign audience, what you say? Game Steam: https://store.steampowered.com/app/1543090/LiBER/

Here's a Fredbear sprite i made in Aseprite! I'm pretty proud of this!

(Btw, sorry for the double post! I'll try to post less)

The Authority decided to investigate the Basement and leads to recovering classified documents Fun fact: The building has 928 doors and not 1000 doors. #horror