Aurora lights, using shader graph in Unity

Hello everyone, welcome back.

Today, we will going to look at how to create a natural phenomenon called aurora, also known as polar lights or northern lights, in Unity using shader graph.



Intro

For this I am using Unity version 2022.2 but it will work on any Unity LTS versions. I have a project in URP. I have setup the skybox material and I am using skybox shader from this blog.

Today's effect is the extension of this blog. They are the prerequisite for this this effect, so if you have not seen them already, I encourage you to do so.

Then I have the post processing volume with modules Bloom and Tone mapping with the mode Aces.

Effect break down

We can break aurora effect down in 4 steps.

  1. They have these gradient colors, majorly consisting of green, light blue, cyan and magenta.
  2. The intensity of the colors differ along various part of aurora waves.
  3. They do not have any specific shape.
  4. Lastly, they distort or kind of waves in the sky.

So we will try to replicate those in our shader graph.

We will create a separate sub graph for our new effect. So let's first create a sub graph.

Right click > create > shader graph > sub graph. We will call it AuroraSubGraph.

In our sub graph, select the Output node and in the Graph inspector, Go to Node settings tab and we will give a proper name. Let's call it Aurora. We will output the color here so keep the type Vector4.

Creating the gradient

Let's deal with the gradient part first, so we will create Gradient node.

Unfortunately, we cannot currently expose the gradient in the inspector, so we are forced to define the gradient colors in shader itself.

I am going to set the colors I mentioned earlier but you can set the colors of your liking. We can use blend mode Classic or Perceptual.

Now, to work with gradient we need to sample it, so we will take its output and feed it into Sample gradient node. Here, the Time input has a valid range of 0 to 1.

If we feed 0, it will take color from the very left of our gradient, if we feed 1 it will take color from very right, and everything in between correspond to the colors in between.

Let's create a UV node. UVs are basically Vector2 with 0,0 at bottom left and 1,1 at top right. We will take its output and feed it in Split node.

The R will be X and G will be the Y. Now we like to have an ability to offset this gradient so let's take the Y and we will add some float value to offset it and then finally feed the Add node output to Time.

Now we can offset it by changing that value.

We want to change it from the inspector so let's create a float property, call it GradientOffset, drag it in and feed it into Add node.





Creating waves

It is time to create some waves, for that let's create another Gradient node. Let's set some random black and white colors.

Just make sure you keep first and last color exactly the same, otherwise it will get a weird cut in our waves.

Let's sample it using Sample gradient node. then take our sampled gradient and we will feed it into Power node. Power node will darken the values which are less then 1 as we increase the power.

We want to control the power from the inspector so let's create a float property, we will call it AuroraPower. Give a default value of 1, drag it in and feed it into Power node.

Now we have these waves, but they have the same intensity across.

We will use the Simple noise node, we want to control this Scale from the inspector so let's create another float property, call it NoiseScale, give a default value of like 20, drag it in and assign it to Scale.

Now we will take its output and feed it into Multiply node, now we can control its intensity. We will control this from the inspector so let's create a float property, call it Intensity. Give a default of 1, drag it in and feed it into this Multiply.

Then we will multiply Power node output with Multiply. Now I want to stretch this noise so let's create a Tiling and offset node. Take its output and feed it into Simple noise node's UV. Then in the tiling let's set some smaller number in Y.

Let's multiply this with our gradient colors, then feed it into the output node.



Now, we want to pan these waves, to do that let's create another Tiling and offset node. Tiling and Offset node has the same UVs as our UV node, so we can use its output as Time.

However, when we change this Tiling and offset, it can output values which are less then 0 or greater then 1, but our Gradient node's Time only accepts value between 0 and 1.

To fix it, we just use Remap node. We will remap the values to 0 and 1, however we don't know what the original minimum and maximum value is.

So I will take Tiling and offset node's output and feed it into Sine node. It will output between -1 and 1. Then feed it into Remap node and finally take this output and feed it into the Split node, it then take its Y, and feed it into Time.

Let's create a Vector2 property, call it ScrollSpeed, give some default value and drag that in.

Let's also create a Time node, take its Time and multiply with our ScrollSpeed, Then take its output and feed it into offset.

We will also control this tiling from the inspector so, you guessed it, let's create another Vector2 property, call it Tiling, Give a default value of 1,1. Drag it in and feed it here.






Distortion of waves

For distortion, let's create a Twirl node, then take its output and feed it into Tiling and offset node's UV.

The Center is the center of the effect and Strength determines how strong the effect will be, we will control the Strength from the inspector so let's create a float property for that, call it TwirlStrength, give a default value of some small number, drag it in and feed it into Strength.

We will also control the Offset from the inspector so create a Vector2 property, call it TwirlOffset, drag it in and feed it into Offset.

To distort this I will use this Center, so let's create another Simple noise node. Take its output and feed it into Center. We will change this Scale dynamically overtime.

For that let's create a float property, call it DistortionSpeed, give some small value as a default, drag it in. We will multiply it with the Time. Then take its output and feed it into Sine node. It will continuously ping pong between -1 and 1.

Then we will take its output and remap it using Remap node. It remaps this value, which has original range of -1 to 1, and remap them to this Out Min Max.

We will control it from the inspector so let's create a Vector2 property. Call it DistortionMinMax, I will give a default of 10,12. Drag it in and feed it into Out Min Max. So now this value ping pong between 10 and 12.

Finally we will take its output and feed it into this Scale.



Now, the texture will apply from the XY plane, and our skybox is basically a sphere so it will try to wrap around it and we may encounter weird stretching so to avoid that We will apply our texture from XZ plane.

To do that, we need axes information, so we need a Position node, we will keep the space World, then we will normalize it by feeding it into Normalize node.

Now we have axes information we will Split it, we want to apply texture from XZ plane so we will create a Vector2 node, feed our X and Z, and finally take this output and assign it to this Twirl node's UV.

Our sub graph will finally look something like this.




Bring it together to our skybox shader

Now in our skybox shader graph, drag our sub graph, then define all the properties for the inputs and assign them.

We want to apply our effect in sky so we will take the Clamp node output and feed it into new Power node so that we can control the blend. We will control it from the inspector so let's create a float property, call it AuroraBlend, give a default value of 1, drag it in and feed into Power node.

We will multiply it with our sub graph's output.



then add it with our final Add node and finally feed into our Base color block.

It will look something like this at the end.







That's pretty much it, we have our aurora lights skybox shader.

Trouble following along? Check out this video.



Thank you so much for reading.

Comments