Camera Facing Health Bar System With ShaderGraph

Austin Mackrell
6 min readMar 23, 2021
Note the “Billboard” effect that keeps it constantly facing the camera!

Today we are going to look at building a health bar, that constantly faces the camera. This is called a “billboard” effect, which is commonly used in particle systems. We will accomplish everything in ShaderGraph, including the billboard. I followed two separate tutorials to get the health bar working, and then the billboard working, then made some modifications to get them working together. I will link both tutorials below if you want to check them out directly, but also do my best to explain exactly what is happening. A quick disclaimer that I am by no means an expert in shaders or ShaderGraph, I will do my best to show what is going on behind the scenes, but most of that knowledge was found by tinkering. While the graph is working as intended I am still trying to grasp everything going into it. Here are the two posts I followed to get this running:

Billboard: https://answers.unity.com/questions/1621611/how-to-do-billboard-shader-with-shadergraphhow-to.html

I followed the top answer here, by insominx.

Healthbar: https://devsplorer.wordpress.com/2020/05/18/creating-a-3d-progress-bar-with-shader-graph-devlog-tutorial/ by Ese

Lets look at the billboarding first.

I know it looks intimidating, but lets break it down, I’ve labelled each node so we can follow them in order.

  1. This position node is what we will be modifying to accomplish our billboard. We want the sharers position to always be facing the camera.
  2. This is an object node, it holds the object itself position and scale.
  3. We multiply the position node by the scale, in order to have our shader scaled correctly. If this is not done, you will just have a 1 by 1 square rather than a bar.
  4. We the split the vector 3 up, this is important as we actually need it to be a vector 4 for later.
  5. This is a Vector4 node (sorry my 5 is slightly covering it). This will take our split up vector 3 and turn into a vector 4. The w value will just be 0 as we did not have one in our vector 3.
  6. This transformation matrix node is feeding in the inverse values of the cameras view. This is how we know how much to rotate the object based on the view so that it is always facing forwards.
  7. We now multiply our position with that inverse view to make it face towards the camera.
  8. We need to feed in our objects physical position to the newly rotated shader position.
  9. We add the two together to apply that positional change.
  10. Our master node only takes object space, so we need to convert from world to object space with this Transform node. This part is a bit confusing to me as we were in object space the whole time. My guess would be that the inverse view is converting it into world space. Either way, it doesn't work without this node so go ahead and put it in.
  11. Lastly we plug it into the Vertex Position slot of our master node, and were good to go.

If we apply this to a quad, change the scale of the quad to look like a rectangle and spin the camera it should always face us.

Now we to create the health bar effect:

Once again lets break this down.

  1. If you checked out the tutorial linked for this one you will notice right off the bat that we are using a UV node instead of a positional node. Since we are constantly changing the position data in the graph, if we use that positional data for the health bar we get a bizarre effect.

Not quite what were going for. So I used UV data instead, to get around the issue.

2. Next we are going to split that UV Data. You can choose which direction our chart fills by using R, or G. R will fill left to right, while G will fill top to bottom.

3. We need to create a fill parameter here. It is just a vector value that we can modify. It will represent our health with 1 being full and 0 being empty

Just hit the +and select vector 1, name it, and then drag into the scene. Set the Default to .5 for now so we can see what is going on with our graph. You will probably want to set it to 1 down the line, so that your health bar starts full by default. Also make sure it is exposed so you can play with it in the shaders inspector!

4. We now compare out UV value with our fill using a compare node. A compare node outputs a bool value. So we are comparing if your UV’s X value, is less than our fill number. If so we will output true, otherwise we will output false.

5. Branch node gets a bit confusing, Im still trying to wrap my head around this one so I will just quote the original tutorial: “What is given in Predicate input works like a boolean map. Where the value is true it puts the value in True input, where the value is false it puts the value in False input. This is where we translate values to a color space. Where FillRate is greater than the point in coordinate space, it is white (1), other side is black (0).

6. We will replace color from the output of our branch, which is black to whatever color we choose.

7. This is a custom color, it can be made in the same way as our vector1 but choosing color instead. We will need two, one for an empty color and one for a full color.

8. This multiply node will multiply the output of the branch white by a color, therefor inheriting that color. The reason we can multiply instead of doing a color replace is that the color white is represented by 1 where the color black is represented by 0. If we multiply anything black we will always get black. Green(0,1,0)*black(0,0,0) = Black(0,0,0). But if we multiply by white we will get whatever we are multiplying by. White(1,1,1)*Green(0,1,0) = Green(0,1,0).

9. This is the color we want to replace white with.

10. We can now Add the two together to get our health bar colors correctly showing.

11. Plug them into the color slot of your master node and you will have a functioning health bar shader!

Lastly lets take a look at modifying at runtime. If you look at our shader properties you will see we have a field for reference.

This is the actual reference to the property. So we could change it in our enemy script by using.

Renderer.material.setFloat(”Vector1_B96A8254", _health/_totalHealth)

This is not the prettiest property name to look at so we can just go into the field in the graph and rename it to “Fill”.

now we can set our health via: Renderer.material.setFloat(”Fill”, _health/_totalHealth)

Much better!

Thanks for giving this a read and I hope it helped you create a nice effect for your health bar!

--

--

Austin Mackrell

A Honolulu based software developer, that enjoys surfing, spearfishing, and making videogames