“Learn how I created a fun, customizable shader in Unity for my latest project! Discover tips on making creative, educational 3D visuals for games.”

Hello Everyone! I’m going to attempt to explain what I’ve got going on here. For starters, I’m working in Unity 2022.3.41f1 with URP (Universal Render Pipeline). I originally thought using an older version of Unity would be more stable, but this version is giving me a few challenges. I’ll explain more on that later For now, let’s dig into this shader. I’m attempting to make a gummy bear texture, for my hero, a gummy bear. I’ve had quite a lot of trouble making this shader, but I think I have something that I’m happy with for now. I’ll have to test it out in game play mode to see how it works during gameplay. I’ll have to update my code to test it out; stay tuned for that fun blog post!
Some of the trouble I’ve had is getting all of the shader elements to work together. This gummy shader is a lot like a thick glass that has a subsurface diffuse scattering on it, refraction, an appearance of a soft outer edge and a specular highlight that is the same hue as the base color, kind of like what you find in a metallic shader. You can see why I was having trouble. I tried all sorts of combinations to get all of those elements to work happily together… and I’ve decided the best approach is to fake it… which at the end of the day, is basically what a shader does anyways. However, this approach may backfire, so I’ll have to test it in actual game play. We’ll get to the testing soon.
Let me explain what I’ve got going on. For starters, I’ve created a URP Lit shader graph and since Unity 2022.3.41f1 doesn’t have a SSS node, I have to create one. To make the sub diffuse scattering I start with a custom node.

Imagine we have a translucent candle. If we have a directional light shining on the candle, some of the light will bounce off, but some of that light will enter the candle wax and then bounce around inside the wax before exiting on the opposite side. It also softens the light transition where the light hits the core shadow. The calculations to recreate this are pretty heavy, so we’re going to mimic it. In order to do that, we need three pieces of information. 1) The light direction (L), we will also need to know the opposite side light direction (-L), 2) We need to know the view direction (What we see, the camera, (V), 3) Lastly, we need the surface direction of the object, also known as the normal (N).
Now that we know what information we need, let’s get to work implementing it. Start out by creating a custom node in your shader graph. Under ‘Type’ select String. We’re not going to need a file just for a few lines of code. After you have selected type, go to the Name section. We’re going to add a name that describes this function. Personally, I prefer the Camel Case naming convention where I capitalize the first letter of each word, but feel free to use the Snake Case if that is your preference. I named my function GetMainLightDirection.
Next, head on up to your ‘outputs’ section. This is where we are going to enter our variables. For the first variable, we are going to name it MainLightDirection. Make this a vector 3. Name the next variable MainLightIntensity. Also, make this a vector 3. Now that we have the variables in place, let’s head over to the body to add our code.
#ifdef SHADERGRAPH_PREVIEW
MainLightDirection = float3(0.5,0.5,0);
MainLightIntensity = 1.0f;
#else MainLightDirection = GetMainLight().direction;
MainLightIntensity = length(GetMainLight().color);
Next, let’s add a Normal Vector node, make sure that the space is left to world. Let’s add these two vectors together. Create an Add node and connect the custom node to the ‘B’ input of the Add node. Before we connect the Normal node, let’s connect it to a Multiply node. This will allow us to control how much the normal effects the light direction. Once the Multiply node has been connected to the Normal node head over to the blackboard. Press the white plus button in the top right corner and add a float. Rename the float to ‘Normal Influence’. Now you’re free to drag it into the main editor window and connect it to the Multiply Node.

Now connect the Multiply node to the Add node. Since we have added 2 vectors together, we have to add a normalize node. This will turn it into what is called a Unit Vector. (You can find out more about normalizing vectors here: https://www.khanacademy.org/computing/computer-programming/programming-natural-simulations/programming-vectors/a/vector-magnitude-normalization#:~:text=To%20normalize%20a%20vector%2C%20therefore,the%20unit%20vector%20readily%20accessible. )
Remember the diagram of the candle at the beginning of this breakdown? We need more than just the light direction. We also need the information on where the light exits the candle. The negative light direction (-L). Add a Negate node and connect it to our Normalize node. Yeah! We’ve completed the first part of our SSS shader function! Let’s group the contents and name it Get Main Light Direction to keep things tidy. Just drag over and select all of your nodes and right click. Choose the group option and rename your group.
Ready? Let’s jump into the back scatter!
Let’s start by creating a View Direction node. Connect this node to a Dot Product node.
*(The name “dot product” is derived from the dot operator ” · ” that is often used to designate this operation; the alternative name “scalar product” emphasizes that the result is a scalar, rather than a vector (as in three-dimensional space).)
In the other input for the Dot Product node, connect the output of the Negate node (From the Get Main Light Direction group). After they have been hooked up, let’s add a Saturate node. This will make sure that the values remain between 0 and 1. Now we have a value that tells us how much scatter we have across the back of the surface. However, we’re going to want to have some control over that back scatter, so let’s add some nodes that will help us do that. For starters, add a Power node and next add a Multiply node. Make sure they are set up in this order, power then multiply.
Okay, now that the nodes have been hooked up, let’s head back to the blackboard. We need to create two new floats. The first float can be named SSS power and the second float can be named SSS intensity.

Now we want to connect the back scatter to the light intensity. In order to do that we need to add one more Multiply node, connect it to the previous Multiply node. Now connect the light intensity from our custom function to the input of the multiply node. Now the light intensity will affect our shader. Group the contents and name it ‘Back Scatter’.
When light leaves an object, it often has a different color. For example, your ears and fingertips will have a red color when light passes through them, because of our blood. A candle will also have a slightly different color, depending on the wax color. Let’s create an input to control that back scatter color.
To start, head over to the blackboard and select the color property, then name it something like ‘SSS Color’. Now create a new Multiply node. Connect this node to the last multiply node in the backscatter group. Drag your new SSS color property from the blackboard into the main editor and attach it to the other input on the Multiply node. Once it has been attached you can group this node and the color property and name it ‘SSS Color’.
Look at us, cruising along! Okay, let’s dive back in. I’m sure you’ve noticed that a really thick object wont have as much back scattering as a thin object. As far as I am aware, during run time, there is no easy way to calculate this… so let’s fake it with a texture map. There are some types of software, such as Substance Painter, that can actually bake thickness maps. However, if you don’t have access to Substance Painter, you can make a rough map by inverting a baked ambient occlusion texture. Head back over to the blackboard and create a new 2D texture property. Name this property something like “Thickness Texture.” Now drag this property on to the main editor. You will want to make a Sample Texture 2D node now. Connect your new Thickness property to the texture input on the Sample Texture node. Now add a Multiply node. Connect your R(1) output of your Sample Texture node and the output of your SSS Color group to your new Multiply node. We want more light to pass through the thinner parts of the object so we’re going to have to invert the texture map using a One Minus node. Place this One Minus node between the Sample Texture 2D node and your Multiply node.

You can now group this selections contents and name it ‘Thickness’. *Note: Substance painter uses white to represent the value of 1. You may not need the One Minus node if your values are already inverted.
Almost there. Let’s talk about the main color. We’re going to blend the Backscatter color to the color on the surface of our object.
To do this we need to create two new properties on the blackboard. One property will describe the Albedo texture (use a texture 2D property for this one) and a new color property. Name this new color property something like ‘Base Color.’ Now we are going to create a new node, it will be another Sample Texture 2d node. Now plug the Albedo texture into the texture T2 input. Next, add another multiply node and connect the Base Color property to input ‘A’. The Sample Texture 2D node can plug into input ‘B’. Now we’re going to add this information to the output of the Thickness group. To do this, let’s create an Add node. Connect the output of our current Multiply node to the ‘B’ input in the Add node. Then plug in the output of the Thickness group into the A input of the Add node. You can now group this selection and name it something like ‘Final Color’. Plug the output of your final color into the base color input of your master stack.

We actually have to add a few more parameters before this component of the shader is complete. Hang in there, we’re nearly there now. Next, let’s head back to the blackboard and create another Texture 2D property. Name this property something like AO (for ambient occlusion) texture. Drag the property into the main editor and then add a Sample Texture 2D node and connect them via the textureT2 input. Now connect the output of this node to the ambient occlusion in your master stack. Also, in the blackboard, create another new property. This new property will be a float; name it ‘Smoothness.” Drag this property into the main editor and connect it to the smoothness input of your master stack.
Let’s test out what we have so far. Save your asset and head over to your scene view. Now click create > material. This will create a new material based on our new shader. Start to change all of the default values from 0 and change the black colors to colors that are appropriate for your object. Your material should now start to react to the light position and intensity. Good work! We have a successful SSS Shader!
Let’s continue to build on this shader working towards our goal of making a gummy like texture. Personally, I’ve tried a bunch of different things and I’m still not getting the look I want. Perhaps I’m overpowering the shader a bit? Ahhh… oh well, I’ll tell you what else I’ve got going on in the little bear at the top of the screen. If anyone else has any ideas, feel free to let me know in the comments.
The next part that I’ve added in is an inner cube map with a Fresnel distortion. I’ll show you how I did that. Let’s get started!

Here is a copy of the gummy bear sky box I’ve made. It’s kind of a golden colored marble texture. I have two versions. A more crisp version where the marble veins are more pronounced and this softer version. I think this softer version will look better, but let’s test them both out. Once the skyboxes were created, I added them to the materials folder of my project. In the inspector, you can change some of the cube map’s parameters such as texture shape.
Now, just like before when we calculated the main light direction, we are going to start off with calculating the viewer direction.

Let’s start off with the Position node. This represents the pixel that we want drawn. Make sure it’s set to world space. Next, were going to need the position of the camera. We’re going to use a Subtract node to get the direction in which we are viewing the character. This is now our vector. Now we want to use a Normalize node so that we can get the vector to a length of 1.

Next we’re going to calculate the direction of the camera movement. Ha ha ha… okay, that sounds super confusing. I think of it like this, the camera is your head. The initial direction describes where your head is at and the direction influence describes where your head (face) is pointing, and with the Camera Direction Influence property, where your eyeballs are pointing. I hope that helps. Okay, let’s plug in the eyes. Start with a Camera node. This time, instead of position, select direction and plug it into a Multiply node. Next, create a Camera Direction property. This will be a float. Plug it into the Multiply node. Now we are going to want to add these two calculations together. Create an Add node and plug in your Camera Direction Influence and your Calculate Initial Direction. Great work! Now let’s have some fun with Fresnel lens effects.
Leave a Reply