Moon skybox in Unity3D using shader graph

Hello everyone, welcome back to the channel.

Today’s post is the new episode of the skybox series, In the day and night cycle post, I have been asked how we can apply texture on our Moon, and generally I would reply how we can do so but this one was bit tricky so here we are.

For this, I am using Unity version 2023.1 and I have a project setup in URP. I also have the post processing volume with main modules Bloom and Tone- mapping with the mode ACES and we are going to use DayAndNightShaderGraph from our previous episode, if you haven’t checked it out already, I highly recommend you to do so.

And with all of this out of the way, let's get shading.



Intro

Okay, we have this shader graph, and I have simplified it a bit so we can get going without being overwhelmed.



Okay, the MainLightDirection is the forward axis of our Directional light which we are feeding from the script.

Applying the moon texture

To apply the texture, let’s create Texture2D property, call it MoonTexture, let’s give default texture.

I am using this texture for moon, provided by NASA but you can definitely use the cheese texture. Make sure to check Use tiling and offset, and drag it in.

We will sample it using Sample Texture2D node then we will multiply it with our Step node then take Multiply node’s output and multiply that with our MoonColorIn the scene view, adjust our tiling and offset for the texture.

Keeping the texture in place

Now to keep the texture in place, we need all 3 axes of our directional light.

We already have the forward axis as MainLightDirection. Now technically we can calculate up and right axes based on that but to keep things simple and we are already using a script so, I am going to create 2 Vector3 properties MainLightUp and MainLightRight and feed them via script.

using UnityEngine;

[ExecuteInEditMode]
public class GetMainLightDirection : MonoBehaviour{
    [SerializeField] private Material skyboxMaterial;
    
    private void
 Update() {
        skyboxMaterial.SetVector("_MainLightDirection", transform.forward);
        skyboxMaterial.SetVector("_MainLightUp", transform.up);
        skyboxMaterial.SetVector("_MainLightRight", transform.right);
    }
}

Now we have all 3 axes of our directional light, but we cannot use them as it is. We need to combine them and for that let’s create a Matrix construction node.

Matrices are basically a collection of row and columns that stores positional and rotational data of given object. Matrices are useful to perform operations like translation, rotation, scale, shear on Vectors.

Now take our MainLightRight and feed it into M0, MainLightUp to M1 and MainLightDirection into M2. Now we have a rotation matrix, but it outputs matrix and we need Vector3, so let’s take the 3x3 matrix and feed it into Multiply node. We are using 3x3 because we not dealing with translation and also make sure that we feed it into input A of our Multiply node. Now let’s multiply it with our View direction.

We have rotated our view direction and to apply our texture perpendicular we will just take our Multiply node’s output, and feed it into the UV of our Sample Texture2D node and now our texture will stay in place.



Moon phases

One final thing, there won’t always be a full moon, so let’s recreate moon phases. For that let me duplicate the moon nodes particularly Dot product, Arccosine and Step nodes. This will be our second moon and I want to rotate it a bit from the original so let’s create a Rotate about axis node.

Rotate about axis node will rotate In Vector, around this Axis about the Rotation angle in radians.

Let’s take our MainLightDirection and feed it into In then take MainLightUp and feed it into Axis of our Rotate about axis node. Then take our Rotate about axis node’s output and feed it into the Dot product node.

Okay, so we have 2 moons, I will use the 2nd moon and subtract it from the original to mimic moon phases, it’s not super realistic but will give you the ideas.

We already have the angle in Arccosine and we are using the MoonSize as an angle threshold. So, MoonSize is basically the angle from the center point of our moon to the edge aka radius.

So if I want rotate the 2nd moon so that it touches the edge of the original one, I need to rotate it 2 times of our MoonSizeSo in the shader graph, take our MoonSize, multiply it by 2.

Now we want to rotate the 2nd moon to both sides so let’s take our Multiply node’s output and feed it into Negate node. Negate node will multiply any value we feed in with -1.

Now let’s create a Lerp node. Lerp stands for linear interpolation and it will interpolate between input A and B based on input T, which has a valid range between 0 to 1. So if we feed 0 into T we will get A as output, if we feed 1 into T we will get B as output, if we feed 0.3 into T we will get 30% from A and 70% from B and so on.

Now take our Negate node’s output and feed it into input A and take our Multiply node’s output and feed it into input B of our Lerp node.

For out input T, let’s create a Float property, call it MoonPhase, set the Mode to Slider which goes from 0 to 1. Drag it in and feed it into input T. Then take our Lerp node's output and feed it into Rotate about axis node's Rotation.

Now we will subtract our 2nd moon from the original so take the output of our Step node from the original moon and feed it into the Subtract node. Then take Step node's output from 2nd moon and feed it into Subtract node.

Now this subtraction will be absolutely fine where our moons are intersecting but we will get negative values where our 2nd moon is not intersecting. To fix that we will take our Subtract node’s output and feed it into Saturate node. Saturate node will basically clamp the values between 0 and 1. Finally take Saturate node’s output and feed it into this multiply.



That’s it for our shader, it will look something like this.



And with that we have our textured moon, pretty cool.

Trouble following along? Check out this detailed video tutorial!



Thank you so much for reading!

Comments