Unity ShaderGraph Procedural Skybox (Tutorial)

screenshot-2019-09-05-at-18.09.45.png

Unity ShaderGraph Procedural Skybox (Tutorial)

Create the sun and the sky,
And the stars and the clouds,
Add them all up,
And turn day into night

0.Introduction

Turns out..it’s surprisingly easy to start creating your own custom skybox shaders with Unity + ShaderGraph…
In this tutorial we’ll build up a fully customisable skybox by creating functions in ShaderGraph for each different layer the skybox consists of, starting with the colors of the sky, then we’ll add the sun and a layer for the clouds and the stars and blend between day and night by the rotation of the directional light in the scene.

This tutorial was written using Unity 2019.2.4f1 using the LWRP template.

1. Creating the Skybox Material

1.1 Create a new unlit ShaderGraph

Right click in the Project window and select Create > Shader > Unlit Graph

1.2 Create a new material

Create a new material in the Project window and drag the ShaderGraph on top of it to assign it to the material.

1.3 Assign the material to the skybox

Open Window > Rendering > Lighting Settings and drag the material into the Skybox Material slot in the Environment settings.

1.4 Open the ShaderGraph

Double click on the Skybox ShaderGraph file in the project window to open the graph…

1.5 Simple gradient sky Test

Just to make sure everything works we can create a very simple gradient skybox by using the green channel (Y-axis) of the normalised world position and feeding that into a sample gradient node. The normalised world position ranges from -1 at the bottom of the world to 1 at the top center of the world so in order to plug that value into the time input of the gradient it needs to be remapped to a 0 to 1 range first.

Screenshot 2019-09-01 at 15.45.42

Screenshot 2019-08-24 at 16.15.06.png

It would be really great if we could use gradients throughout the entire skybox but at the moment gradients in ShaderGraph cannot be turned into exposed properties so for the sake of not having to open the graph to make adjustments we’ll use three separate color properties instead and blend between those..

2. Adding a Sky Layer

This ShaderGraph nodes setup blends three exposed HDR color properties for the sky, horizon and ground. The softness of the gradient towards the horizon from the ground and from the sky can be adjusted with the Exponent1 and Exponent2 properties, the overall brightness with the Intensity property. By using HDR colors we can make the sky emit light coming from the middle horizon color for instance. Using HDR colors combined with a Bloom post-processing effect on the camera can make it look even better!

Screenshot 2019-09-08 at 15.03.46.png

Screenshot 2019-09-01 at 17.15.24.png

3. Adding the Sun

To add a sun to a skybox that has its position based on the direction of a directional light in your scene, a custom ‘Main Light’ node that gets the direction of the Main Light in the scene has to be created first..
You can find detailed instructions on how to create this custom main light node (and custom nodes in general) in this Unity blog post (opens in a new tab).: blogs.unity3d.com/2019/07/31/custom-lighting-in-shader-graph-expanding-your-graphs-in-2019/

The best way to create a custom node that we can reuse in other graphs as well is to create a Custom Function node first and then convert it into a Subgraph.
Both the custom node function .hlsl file and the SubGraph can be downloaded from here (see the Subgraphs and CustomNodes folders): github.com/Timanious/MyShaderGraphs/tree/master/MyShaderGraphs_Project/Assets/_ShaderGraphs

Or if you want to create this custom node yourself, you can follow along with these instructions to create the Main Light node…

3.1 Create a Custom Function node

Right click in the ShaderGraph working area and create a new Custom Function node, you can find it under ‘Utility’:

Screenshot 2019-09-03 at 11.59.58.png

3.2 Add input and output parameters

Click on the settings cogwheel from the Custom Function node and add the following input and output parameters:Screenshot 2019-09-03 at 12.08.20.png

3.3 Function name

The name of the function inside of the .hlsl file that this node is going to use is called MainLight, type that into the name field:Screenshot 2019-09-03 at 12.31.14.png

3.4 Adding an HLSL include file

As you can see the Custom Function node has the option to use a inline function if you switch the type to string, (see the Unity blogpost for more information about that) but using a separate HLSL include file gives us more flexibility, it can contain more complicated functions and you can keep them all organised in one place.

At the time of writing this, Unity doesn’t have a convenient menu item for creating an HLSL include file asset, so you’ll have to create it yourself, for example by creating a normal unlit .shader file and changing the file extension to .hlsl and removing the code from it..Create a file named CustomLighting.hlsl and paste this code into it (link opens in a new tab): github.com/Timanious/MyShaderGraphs/blob/master/MyShaderGraphs_Project/Assets/_ShaderGraphs/CustomNodes/CustomLighting.hlsl

3.5 Assigning the .hlsl file

Drag the CustomLighting.hlsl file into the text asset Source slot to assign it to the custom function node:Screenshot 2019-09-03 at 14.16.33.png

3.6 Convert to Sub-graph

Right-click on the Custom Function node and select ‘Convert to Sub-graph’ and name it MainLight:Screenshot 2019-09-03 at 14.18.41.png

3.7 Add Sub-graph Outputs

Open the new Subgraph and Connect the WorldPos input from the Custom Function node to a Position node. Also create 4 new inputs on the Output node corresponding with the outputs from the Custom Function node:Screenshot 2019-09-03 at 14.22.20.png

3.8 Organising the Sub-graph node

If you look at the Blackboard, you’ll see that underneath the name of the Subgraph in a darker tint grey you can specify where the subgraph is organised in the node creation menu:Screenshot 2019-09-03 at 14.29.22.pngIf you change it to Input/Lighting it will be sorted with the other Input/Lighting nodes, so it will be easy to find:Screenshot 2019-09-03 at 14.31.38.png

3.9 Positioning the Sun

Now that we can get the direction of the Main Light in the scene, we can use it for positioning a sun:Screenshot 2019-09-03 at 14.47.30.pngThis graph works by getting the dot product from the view direction and the inverse direction of the main light. The size, intensity and softness of the sun can be adjusted with the radius, intensity and exponent variables. The color of the Main Light in the scene is also used to give the sun color.

3.10 Add the Sun to the Sky

The last step is to simply add the output from the Sun to the sky color with a Add node:Screenshot 2019-09-03 at 14.57.27.pngScreenshot 2019-09-03 at 15.13.57

4.Adding a clouds layer

Building up a clouds layer has a lot of steps, but it starts simple, so for this I think it’s best if we build it up step by step, then you can decide how much information your clouds layer needs.

For the purpose of better showing you what’s going on this tutorial uses an RGB circles testing image created with Photoshop for most of the examples as well as a noise/clouds texture + normal map created with CatlikeCoding’s NumberFlow Unity plugin (which is also a node based application but for creating procedural textures).
You can download the textures used in this tutorial from this blogpost: https://timcoster.wordpress.com/2019/09/09/tileable-clouds-texture/

Or if you are going to use your own textures, then what probably works best is using an actual transparent .PNG file instead of a black and white one. This way you won’t get clouds with grey edges but with transparent white edges instead. Also make sure the texture has an Alpha channel:Screenshot 2019-09-06 at 12.38.34Screenshot 2019-09-06 at 12.43.27

4.1 Adding a clouds layer

The node setup below generates an infinitely large flat layer for a clouds texture:Screenshot 2019-09-06 at 12.34.42Screenshot 2019-09-06 at 12.03.52

We can blend the transparent clouds texture with the sky colors and the sun, using a blend node. By using the Alpha channel from the texture for the opacity of the Blend node and setting the Blend node to overwrite, we can get a nice clean result:

Screenshot 2019-09-06 at 13.02.41.pngScreenshot 2019-09-06 at 12.57.38
4.2 Adding far tiling

By adding a property for far tiling we can make it look like the horizon is a little bit closer.
for the RGB circles example it was set to 0.1 and for the clouds to 0.25:Screenshot 2019-09-06 at 13.08.31.png

4.3 Adding Texture Tiling

Adding a texture tiling property gives control over the size of the texture, here it was set to .05:Screenshot 2019-09-06 at 13.22.23.png

4.4 Adding Texture Offset

Adding a Vector2 clouds texture offset property. This will be useful for animating the clouds later on:Screenshot 2019-09-06 at 13.30.50.png

4.5 Adding Cutoff

Adding Distance and Cutoff properties to control fading out the clouds towards the horizon. For the examples below the distance was set to 0.8 and the cutoff to 25:Screenshot 2019-09-06 at 14.03.04.png

4.6 Adding Opacity

Opacity can be added by Multiplying the output of the Alpha channel of the clouds texture with a new Vector1 clouds opacity property. Example shows the opacity at 0.5:

Screenshot 2019-09-11 at 12.16.17.png

4.7 Adding Normal Map

Adding support for a normal map texture to suggest depth based on the direction of the light. This Makes it look like shadows are at the bottom of the clouds when the directional light shines straight down. When adding the Sample Texture node for the normal map, make sure to set the node type to Normal. Also make sure to set the clouds normal map texture property to ‘Bump’ mode on the blackboard.
The custom Main Light function node is used outside of the subgraph here because it uses the tangent position instead of the world position. You can simply copy the custom node from the Main Light subgraph.

Normal map strength was set to 0.8 for the example.:
Screenshot 2019-09-11 at 12.32.26.png
CloudsNormalMapped

4.8 Adding Brightness

Add a new Vector1 property for clouds brightness, and a new add, subtract and multiply node.
Subtract 1 from the brightness value and then add it to the output of the blend node.
Subtracting one from the brightness will make it so that a brightness value of 1 will be normal and a value of 0 will be no brightness at all, instead of zero being the normal brightness.
Also multiply the normal map strength by the brightness property before plugging it back into the blend node, to make it dependent on the brightness value.
Left and right examples show brightness values of 0.1 and 1.5:

Procedural Skybox Add Brightness

4.9 Adding Directional Light Color

Multiplying the texture by the Main Light color output will make the color of the clouds dependant on the color of the directional light in the scene. For the examples the light color was set to light orange and light blue:

Screenshot 2019-09-13 at 13.46.27.png

4.10 Reorganise Layers

To make the adding of the different layers make more sense we can rearrange the graph so the order will be Sun + Sky + Clouds. This way what is closest by will be added last:

SunSkyClouds.png

5. Adding a Night-Sky

Now that we have a sun sky and some basic clouds we can start reaching for the stars!…(yeah, yeah..)…
But in order to see those stars better when we add them it will be handy if we can automatically transition from day-sky colors to night-sky colors using the direction of the sun for the blending between them.
We can do this by using almost the same position input that we use for the sun to Lerp from color a to b, and while we’re at it we can do the same with the sky exponent 1 and 2 properties to make the horizon look thinner at night.

So before we add the stars we’ll have to revisit the sky colors layer..

5.1 Adding night-sky properties

Add three new Color properties for the night-sky colors and add two new Vector1 properties for the night-sky exponents:

Screenshot 2019-09-08 at 15.05.59.png

5.2 Lerping Colors

Swap the three color and two exponent property nodes with Lerp nodes and connect them in the way that you see in the diagram below.
First image is the old sky and the second is the new sky layer with all the new nodes selected so they show a blue line around them:

Screenshot 2019-09-08 at 15.03.46

Screenshot 2019-09-08 at 14.57.17.png

ProceduralSkyBoxDayNightTrans

6. Adding Stars

Now that we can transition to dark night colors we can start adding the stars layer to the skybox. This tutorial uses a very simple black and white stars texture, but you can go as fancy as you want of course:

StarsBlack.jpg

6.1 Sphere Mapping Space

The following node setup gives us a nice spherical mapping for a stars texture. There are some weird visual artefacts from the texture not being made to wrap around a sphere. That’s not really a problem because the texture will be faded out towards the horizon:

Screenshot 2019-09-08 at 16.36.06.png

6.3 Add Intensity

Multiply the RGB output from the stars texture by an intensity value to control how bright they shine:

Screenshot 2019-09-08 at 16.43.05.png

Screenshot 2019-09-08 at 17.21.23

6.4 Add Texture Offset

Adding a Vector2 offset value to the UV coordinates before plugging it in the texture gives us control the position of the stars texture, useful for animating later on:

Screenshot 2019-09-08 at 16.44.28.png

6.5 Add Horizon Fade-out

To gently fade out the stars towards the horizon we can multiply the texture again by using a power node and a fadeout property:

Screenshot 2019-09-08 at 16.46.04.png

Screenshot 2019-09-08 at 17.23.53

6.6 Add Day/Night Transition

Finally add a day to night transition by multiplying again by a Lerp node using the dot product of the light direction for the Lerp node’s time input. Now the stars will be completely visible when the directional light is shining straight up and completely invisible when shining straight down:

Screenshot 2019-09-08 at 16.47.40.png

6.7 Adding the Stars Layer

Rearrange the graph so the stars come on top. They are farthest away so the stars layer is drawn first:

Screenshot 2019-09-08 at 17.02.21.png

Adding everything up and using it on a small testing scene with a floor, exponential fog with a very low value of about 0.0006 and some post processing effects enabled it already starts to look pretty game-ee!:

ProceduralSkyBoxStarsTransition.gif
This tutorial is a work in progress and since the last update there have been made a couple of changes to the clouds layer. Steps now also include the adding of brightness, the color of the directional light and a clouds color. If you don’t have those in your skybox then skip back in the tutorial and add those functions to your clouds layer!

Next update will probably be the adding of a sunset and a small C# component to make the directional light’s intensity be dependant on the direction as well.

Stay Tuned…

p.s Feel free to post any comments or suggestions in the comments!

One thought on “Unity ShaderGraph Procedural Skybox (Tutorial)

Add yours

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Powered by WordPress.com.

Up ↑

%d bloggers like this: