Fabrizio Bergamo

Real-Time FX & Tech Art

Billboard Material Function

Introduction

This post is not a step-by-step tutorial, but more like a documentation of a Material Function I created. However, it does contain screenshots of the entire graph, so feel free to recreate your own version.

I’m currently working on a simple billboard baker that bakes the various GBuffer layers as textures using Render Targets, which can then be used in conjunction with a plane and a material that makes it always faces the camera. 
Of course, the material needed some logic that rotates the plane to align it toward the camera, I wanted a function that would deal with calculating both the World Position Offset and the correct Normals. There are a lot of different ways to achieve this, I found a lot of resources online about it and after some exploration, I gathered together the logic needed for 2 types of billboards:

  1. Free Rotation
  2. Z-Axis Constrained

Geometry Plane Setup

Both logics are set up to work with the default engine plane rotated by 90 degrees on X, but you can make your own one, the only thing to be careful about it’s the plane orientation and UVs. 
For example, I made a version where the pivot is at the bottom so that it’s easier to place it around the scene.

The Function

Here’s the graph:

It’s divided into 2 parts, one logic makes the plane face the camera from all angles (Free Rotation), while the second one has the plane’s Z-Axis locked so that it doesn’t pitch (Z-Axis Constrained). For both versions, there is a switch that calculates the correct normals with and without using a texture.
The function outputs both the WorldPositionOffset and Normal to plug into the Material Attribute.

Inputs

The function has 4 inputs, one of them is a Vector3 for the texture input, while the other 3 are booleans that toggle various switch nodes.
Here’s a description of each of them:

  1. Free Axis (StaticBool) → Whether or not the function should use the Free Rotation or the Z-Axis Constrained logic
  2. Tangent Space Normal (StaticBool) → Defines if the output Normal should be in Tangent Space or World Space
  3. Use Normal Texture (StaticBool) → Tells the function if you’re feeding a Normal Texture, or if it should calculate only the Normals of the plane
  4. Texture (Vector 3) → Tangent Space Normal Texture input

Free Rotation Method

For the free rotation setup, I’ve followed the logic showcased by Visual Tech Art YouTube channel in one of his video, I highly recommend it if you want to understand better billboards, his other videos also cover interesting topics in a great way!

The logic basically takes the UVs of the plane and aligns them to the UVs of the Camera, for the Normals when using a tangent space texture I’ve rotated the vectors by 180 degrees around X to have them as if they were also camera space. I tried to use the TransformVector node set from TangentSpace to CameraSpace but it wasn’t working as expected.

Texture Normals before and after the rotation:

Z-Axis Constrained Method

The constrained logic is inspired by Epic‘s stylized sample project, I think I’ve just cleaned it up a little bit and replaced the custom node used for the rotation with “Arctangent2Fast”.

For calculating the correct Normal I’ve followed the same video mentioned before from Visual Tech Art. While for the Texture Normal I’m applying the same rotation done for the WPO.

Conclusion

Thanks for your attention, I’ve always found confusing the fact that you also need to recalculate Normals when using World Position Offset. This exercise, and exploration helped me to have a better understanding of it and I’m now more confident when dealing with Normals.
I hope you’ve found this post useful, have a great rest of the day!

Resources

Here’s a list of the various resources I’ve used during the exploration and development of the function:


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *