Shader code looks very intimidating, especially if you're an artist like me. I had already started learning programming for a year before I wanted to try shaders a little over a year ago. But shader code is still very different from the C# I got used to.
So my approach was, rather than trying to understand everything at once, to just edit an existing shader in small ways and branch out from there.
Let's look at the shader I first played around with, the ToonLit shader you can get by importing the ToonShading package in Assets > Import Package > Effects (Or download HERE)
Here's what it looks like in the editor:
In the material settings we can see 3 properties, the Main Color, the Base Texture, and the Toon Ramp. Looks pretty simple.
Open up the ToonLit shader
Oof! There's not even that much code, but it's a lot to process.
Let's blur out the parts that are important for the shader to work, but you don't really need to know how this works to get start, you can leave those parts alone.
So what's left?
Right at the top, that's just the shader name as it shows up in the material settings
After that are the Properties that you can change in the material settings
The first part with the "_" is the internal name.
Between the brackets is how it shows up in the material settings.
Color for color picker
2D for texture
After the "=" is what it defaults to.
This is where you have to write the properties again in a slightly different way so the shader can use them in functions (_Ramp is declared above the Lighting calculation that I blurred out)
sampler2D for textures
float4 for colors (red,green,blue, and transparency)
Struct input is where Unity takes your meshes uv sets (and other things we will see later on)
Here it takes the first uv set (uv_) for the base main texture (MainTex) and puts it into a texture interpolator (TEXCOORD0).
So if you'd want to use a second uv set, it would be:
float2 uv2_SecondTex : TEXCOORD1; (add 1 for a new texture)
Now this is where the actual results are made.
The c value is the MainTexture projected over the first UV set, multiplied by the Main Color we choose in the material settings.
o.Albedo(Diffuse) is then set to this c value, making the texture show up on the model
o.Alpha deals with transparency, but this material doesn't use transparency
Here's the whole shader again, hopefully looking a bit less scary and more understandable.
First shader edit
Now the parts of the shader we need are explained, let's edit it.
I made a noise texture in Photoshop, let's add it to the shader.
First duplicate the MainTexture property, and name it something like "_NoiseTex" for the internal name, and "Noise" for the material settings
Don't forget to add the sampler2D _NoiseTex so the function can read it
Saving now reveals the Noise Texture picker in the material settings, so select the texture there.
It won't do anything yet though.
Copy the line that projects the MainTexture as the half4 c value;
Call it "n" for noise, and make it use the NoiseTexture instead. Still using the first UV set.
We still need to add this new projected noise value to the o.Albedo for it to show up, so multiply it with the Main Texture value c.
Save and check out the result.
Yep, that's our noise texture muddying up the meshes over the UV
So how else can the texture be projected? Let's look at the Unity Documentation and see what the Input structure accepts.
World position sounds fun, let's plug that in.
Add "float3 worldPos;" to the Input structure.
And instead of the using the UV's , we use the world Position. It will grab the first two values (X and Y) automatically, which is a side projection.
Save and you will see that the noise is now "stuck" being projecting from one side, even when the model turns. This is how the Triplanar Terrain Shader works
The noise texture can be used in a different way. The value of the RGB channels can be compared and based on that we can, for example, change the color on the mesh.
We need a new color. So we duplicate the main color, rename it, and declare the new name under the Main Color.
Take the n.rgb multiplication out of o.Albedo, and under that check if the value of the red channel of the noise is higher than 0.5 (so gray to white).
If so, set the albedo to be the new _Color2 value;
Save and check out your first little shader effect!
PS: If statements are not great in shaders, its better to replace them with a step function or a ternary operator. But when you're just starting and playing with shaders, don't worry about it yet ;)
If you've been able to follow along so far, and want to challenge yourself to figure out some more effects, you should now be able to do these:
- Replace the 0.5 float value with a slider so you can control the spread of the color. Don't forget to declare the float _SliderValue;
- Instead of a having a color as the result of the noise texture being over a certain value, try setting another texture as result.
- Instead of World Position, set the texture to be projected over the Screen Position (Hint: The Unity Docs on Surface Shader Examples have a screen position example).
- Add the result of the noise comparison to the o.Emission channel instead, this will make it look like its glowing
PS: This is a second draft, will update based on feedback
Links to more shader tutorials:
- Alan Zuconni Alan's gentle introduction to shaders gives a lot more info than I'm giving here, please check it out!
- Ronja's Tumblr Very easy to follow explanations of basics in shaders.
If you want to know what all the things I skipped over mean in bite-size pieces, Ronja can help you!
- Making Stuff Look Good Youtube If video explanations are more your thing, check out Dan's channel that shows you how to make those cool effects you've seen in games like Hearthstone, No Man's Sky, Overwatch, etc.