I do shader work at work and at home, but have only used node based shader editors. I’ve been wanting to learn something new and projects haven’t really been sticking recently, so figured I’d get a little lower level and give HLSL in Unity a go.
I did some real basics tonight, just having a look at the basic surface shader and having a go at changing around some parameters and adding a little functionality.
The first thing I did was change the smoothness input to a roughness one. This is what I’m used to using and seemed like a cool way to look at the various built in variables that the standard surface shader has.
I changed the smoothness variable from SurfaceOutputStandard from _Glossiness (the name of the user smoothness/roughness input) to (1- _Glossiness) to invert the relationship. This let me define the roughness, rather than the smoothness.
After that, I added a colour overlay. For this I had a to add a new user input and edit the albedo.
The input parameters, called properties, are defined at the top of the script and take a display name, type and default value. Being someone who’s used to node based and python scripting, having to be type safe is a bit of an adjustment!
I added this, and then added it to the colour definition, which is then used for albedo.
At first, this wouldn’t work, and I kept getting a variable is undefined error. This was because the properties and actual shader code are separate, and I needed to define the variable inside of the subshader itself.
Once I’d got the hang of those, I thought I’d add some new functionality to the shader with some scrolling UVs.
Again, I added the properties and then defined them in the subshader. The UVs are defined in the Input struct before the main bulk of the variables. A struct is a data type that stores multiple variables in the same physical memory space. Its a bit like a class, but its always public – in C++ (and therefore HLSL) classes are private by default. This means we can access a specific instance of the struct and everything within it is available to us. For example, below we use IN.uv_MainTex, which is the instance of Input that we created when we defined the surf function.
To perform the scrolling, we multiply our scroll speed by time then frac it to take only what’s after the decimal point. This prevents the actual gametime from influencing our shader – we just need it to produce a constantly moving number.
This is really common, and something I’m very used to doing in node based shader graphs. The interesting thing here was that I had to use time.y – why this and not just time? In unity, time is a float4 that contains time/20, time, time*2 and time*3. Its just an easy way of storing a bunch of time related functions that you might be after.
After getting our scroll values, we add them to the original uv coords, The code looks something like this!
fixed2 scrolledUV = IN.uv_MainTex;
fixed xScrollValue = frac(_ScrollSpeedX * _Time.y);
fixed yScrollValue = frac(_ScrollSpeedY * _Time.y);
scrolledUV += fixed2(xScrollValue, yScrollValue);
This was really fun – I learned a lot, even if I was just doing some very basic things! Next step I’d like to recreate a shader that I’ve made in nodes. I’ve been working on an alpha erosion shader recently, so might do that.