Skip to main content

Advanced Billboard Shader + World-Space UI Support

Advanced Billboard Shader + World-Space UI Support

As you know billboarding is basically a plane with a texture on it that always facing the camera.

These are some examples of what we are going for.

This tutorial is going to be pretty straight-forward and easy to follow along you will learn how to make a billboard shader that not only keeps looking at the camera but also keeps its relative scaling intact.

We will also provide an option to keep it rendered on top of all the other objects in the scene. This will be most useful for world-space UI that needs to be rendered on top of other geometry.

We will be making 2 shaders here,

  1. Modified Default Unlit shader :- This one is a general shader ( easy to modify furthur ).
  2. Modified Default UI shader :- This one supports whatever a UI shader supports along with our billboarding capabilities.

 So let's get started with making the first one. As usual create a new Unlit shader and dive into the properties we need.

Properties
{
_MainTex ("Texture Image", 2D) = "white" {}
_Scaling("Scaling", Float) = 1.0
[Toggle] _KeepConstantScaling("Keep Constant Scaling", Int) = 1
[Enum(RenderOnTop, 0,RenderWithTest, 4)] _ZTest("Render on top", Int) = 1
}

Don't get distracted by those shader attributes ( [xyz] ). These are really useful little statements that help us format our materail editor interface. I will be adding another tutorial which goes through all of them in detail.

Now let's look at how these were defined in the CG PROGRAM.

uniform sampler2D _MainTex;
int _KeepConstantScaling;
float _Scaling;

You might have noticed that the _ZTest property doesn't show up here, That's because it goes in the sub-shader state value and while we are there we have to set some sub-shader tags as well.

SubShader
{
Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "DisableBatching" = "True" }
ZWrite On /*Write to the depth buffer*/
ZTest [_ZTest] /*Replaces _ZTest with either 0 for rendering on top and 4 for regular z-test*/
Blend SrcAlpha OneMinusSrcAlpha /*Set how semi-transparent and transparent objects blend their colour*/
Pass {
 .
 .
 .

*Note :

  • We have set 'Disable Batching' to True. This is because if an object is dynamically batched the vertex input that we get will be in world space and we will be writing the vertex shader with the assumption that the vertex data will be in local space.
  • This shader only works with 'Quad' primitive or any geometry which has vertices in the y-axis. So the default plane will not work.

Time for the Vertex Shader

v2f vert(appdata v)
{
   v2f o;
   /*1*/ float relativeScaler = (_KeepConstantScaling) ? distance(mul(unity_ObjectToWorld, v.vertex), _WorldSpaceCameraPos) : 1;
   /*2*/ float4 viewSpaceOrigin = mul( UNITY_MATRIX_MV, float4( 0.0, 0.0, 0.0, 1.0));
   /*3*/ float4 scaledVertexLocalPos = float4( v.vertex.x, v.vertex.y, 0.0, 0.0) * relativeScaler * _Scaling;
   /*4*/ o.vertex = mul( UNITY_MATRIX_P, viewSpaceOrigin + scaledVertexLocalPos);
   /*5*/ o.uv = v.uv;
}

We will go through each line in detail.

  1. If we have _KeepConstantScaling value as false then we don't apply any relative-scaling. Incase we do apply relative scaling then we convert the vertex position from local to world-space and get it's distance from the camera. We assign it to relativeScaler value.
  2. mul( UNITY_MATRIX_MV, float4( 0.0, 0.0, 0.0, 1.0)), We are transforming the origin in terms of the view co-ordinates and assign it to viewSpaceOrigin.
  3. The vertices gets scaled according to our 'relativeScaler' and '_Scaling' values and assign it to scaledVertexLocalPos.
  4. We then add the viewSpaceOrigin & scaledVertexLocalPos to get our view-space transformed vertex positions. Then we apply our perspective projection by mul( UNITY_MATRIX_P, viewSpaceOrigin + scaledVertexLocalPos)
  5. Assign our uv co-ordinates.

There are no modifications to the fragment shader.

To create a fully UI compatible shader we will use the 'Default-UI' shader that Unity provides us and make the same changes. You can get the Unity provided shaders HERE. Just select 'Built-in' shaders from the download drop-down for your desired Unity version.

The source code for both shaders can be found : HERE

That's it! Hope you learnt something. Support Bitshift Programmer by leaving a like on Bitshift Programmer Facebook Page and be updated as soon as there is a new blog post.
If you have any questions that you might have about shaders or Unity development in general don't be shy and leave a message on my facebook page or down in the comments.
For more Shader development tutorials, go : HERE
For Unity development tutorials, go : HERE

Assets Worth Checking Out

POPULAR POSTS

Tri-Planar Terrain Shader

How The Tri-Planar Terrain Shader Looks Like Apart from the manually drawn tile footpath the cliff walls and grass has been generated through a tri-planar shader. How The Tri-Planar Terrain Shader Works The shader that we make will work with the existing terrain system and no need for any custom scripts.
The first two textures provided in the terrain settings will be used to texture the terrain :
*Notes:-
First Texture Index:- Texture that appears on cliff sides.Second Texture Index:- Texture that appears on flat surfaces.Any subsequent textures can be used to draw on top of this as usual.Drawing with either first or second texture slot acts as a eraser removing subsequent textures Terrain Shader Tutorial The shader we will make will be working on top the existing terrain shader that Unity provides us.  So that will require accessing the Unity shader repository for your specific version of Unity.
The version of Unity I used to create the shader is version 2017.4.
Most likely there won&…

Access Reflection Probe Data For Custom Shaders

The Unity shader documentation regarding reflection probes is pretty minimal and not at all comprehensive.
This short tutorial is intended to bring reflection probe functionalities to the forefront your future shader writing endevors which is a fancy way of saying "Look at this cool stuff and go and use it somewhere" 😏
Here we will try just the bare minimum of making a shader that reflects the cubemap data from reflection probe and displays it on the object.

These reflection probes are basically objects that store a complete image of the environment surrounding it into a cubemap which then can be read by shaders to create various effects.
More information on how reflection probes work in Unity can be found here :
Using Reflection Probes In Unity

I am not going over how to set up Reflection Probes here only how to access them inside our custom shaders.
So this is what we will be making:
The reflection probe takes in the cubemap only if it is within it's range otherwise i…

Introduction To Regular Expressions [ Regex ]

What Is A Regular ExpressionRegular expression or regex for short is a sequence of characters that defines a search pattern.
Let me simplify this for you.
Imagine you're writing a huge assignment or a report then you realize you misspelled a word and apparently the word was used a couple of hundred times throughout your document, now any sane person living in the 21st century would :
find and replace->”misspelled word”->”correct word”.

Have you ever wondered how the computer checks for the word?
How the words are magically found and replaced?
It just searches the entire document for “word-to-be-searched” and replace the string with the new one.
Imagine this scenario, you are asked to redact phone numbers from a letter, now you don’t know any of the numbers personally, and since the letter contains plenty of numbers that are not just phone numbers and those should not be redacted what would you do then? This is where Regular Expressions come in to play, a regular expression is bas…

Shader Optimization Part 1

The process of shader optimization can seem like trial and error... in fact, that's how it is most of the time.
Most of the time shader optimizations could be boiled down to educated guesses because each time a shader gets compiled, the GPU driver of that specific hardware is what converts your code into actual machine code, therefore, the machine code generated will be different for each GPU and the driver itself might perform some optimizations on top of your's which won't be available on another GPU, thereby making it difficult to have a standard way of writing optimal shader code.

So the best way to know for sure is to actually test it on the hardware you are targeting.
With that said, Here are some universal best ways of getting your shader to perform better.😅 Do Calculations On Vertex Shader The most commonly used case for this is lighting, an example would be Gouraud lighting, where lighting calculations are done per vertex but at the loss of quality.

Some calculatio…

Fortnite Procedural Construction Animation Shader

Fortnite Construction Shader This shader is loosely based on the one that was presented by the Fortnite developers in their GDC talk: Inner Working Of Fortnite's Shader-Based Procedural Animations.

 Here is what we will end up with:
This technique requires you to author the 3D model in a certain way, More or less how those Fortnite developers did.
So we need the authored 3D model and the shader that uses data we get from the model to achieve the desired effect.

There are some nuances here and there so make sure you don't miss out on the details.😗
The first step will be preparing the 3D model and putting in the required data. I used Blender 2.79 but any 3D modeling software would do.
3D Model PreparationModel It
Apply Vertex Colors: For the direction of flight
Each color is a component of a vector (x, y, z). This will be considered as local space.
Values range from -1.0 to +1.0 for each component.
Negative values are achieved by using values of less than 0.5 and positive values wi…