Implementing a Lit Billboard Shader in Unity Shadergraph

Published

This article is an improvement on our previous article on billboarding to prevent lighting issues for lit billboards and avoid self-shadowing. Previous article on billboard shader is available here.

I was working on a game with 2.5D style like Octopath Traveller, using a custom that does sprite tiling and offset depending on camera position and object forward on the GPU, which I talk about in this article, but when switching to a lit node instead of an unlit node for my quad, there were some glaring lighting issues:

billboard shader shadergraph test lighting
Incorrect Lighting with our Base Billboard

We can see 2 main problems:

  • The quad is self-shadowing
  • Computed lighting is incorrect, backface lighting and front face lighting are reversed

How can we fix this? First we need to adjust our billboarding calculations. I found an article on programmer sought which solves this problem for us.

better billboard shader
The new Billboard Calculations

This shader fixes the self-shadowing issue, but it seems it mirrors vertices position, so our quad gets reversed. A quick fix is to then mirror the UVs afterwards (One minus UV should fix it).

Finally, we need to fix our object normals. There are two simple solutions:

  • If you want constant illumination on both sides, you can set the normals as pointing towards the sky, so a vector3 equals to (0, 1, 0). This is a common solution for grass. See bgolus’s answer in this thread.
  • Set another constant value, or expose a property to point towards a specific object.

In my case, I just set the normals direction in the Vertex shader as equals to the world to object transform of the (0f, 0.25f, -0.75f) vector since I’m expecting the camera to always be in the same position and I wanted my normals to point towards it.

The result is pretty nice in my opinion. Check it out:

rotating directional light
Rotating the Directional Light

We can clearly see the effect of the light rotating, and that the front face becomes darker when the light moves behind the quad (sprite becomes red because of my current post processing settings).

To note: you need to set your shader, and your other quads, as two-sided to ensure shadows are projected when the light goes behind the quad.

Now let’s try some realtime shadows:

billboard real-time shadows
Real Time Shadows on Billboard

Looks like the quad does get shadowed properly by the object in front. With this, we seem to have fixed our lighting and shadowing issues for our billboard shader. Good job!

On another note, I’m thinking of adding a ramp texture for the albedo based on light intensity to get a better control which, among other things, should help prevent the sprite becoming fully dark when the light moves behind the sprite. Stay tuned!