Shenanigans with Shaders

Table Of Contents

For those of you who love rabbit holes, learning graphics programming is a pretty deep one. There’s always some new thing to learn, there’s a bunch of different new languages and toolsets to know, and on top of all that, there’s math. Like anything else in programming, you pick up momentum with each new thing you build, so I found a tutorial and started making shaders. I know very little about this. However, I’m writing what I’m learning, so don’t come for me if I’m off on anything.

Shaders

A shader is a program that runs on the GPU as part of the graphics pipeline. We’re going to focus primarily on shaders in Unity. There are other ways to tackle this, but Unity gives an easy setup to get started quickly. For the context of Unity, a shader is a small script containing logic and calculations for determining the colors of a pixel.

In Unity, we create shader objects which act as wrappers for our shader program. A shader object exists in a shader asset which is just the script we are writing. Creating these in Unity allows for a great deal of freedom in what we make. What we’ll focus on is adding some basic functionality to a shader. We’ll be focusing on using ShaderLab to create shaders.

Setup

The first thing to set yourself up making shaders in Unity is Unity. So download it, and create a new project.

New Scene in Unity

I won’t give a full rundown of Unity and the stuff you can do. I leave that to better minds. In the Hierarchy Window, right-click and scroll to 3D Object and click whichever object grabs your fancy. I always pick sphere for testing stuff. Now we have a 3D Mesh on the screen that we can begin adding things to it. In the Project Window, right-click on the word Assets and create two new folders, Materials and Shaders. Double click into the Materials folder, right-click and Create is right at the top -> click Material. Materials are similar to skins we can apply to 3D objects. We will use this new material to add our new shader to the 3D Mesh. After that, drag our new material into the Scene Window where our sphere is and onto the sphere we made. Now right-click our Shaders folder scroll to Create -> Shader -> Standard Surface Shader. Click the sphere in the Scene window to bring up the Inspector Window. Finally, drag the shader file over to the inspector window with our sphere covered in our new material. We have just applied our shader to the materials. You should see this in the Inspector Window.

Test Material inspector window

Now go back to the Project window and double click our new Shader file. Unity will launch an IDE for use to check out the code. You can configure your choice of IDE; I have VSCode configured. Open the Shader file, and let’s check out the code. I created some basic shader code you can use.

Shader Code

Here is the complete, minimal shader code:

Full Shader Code

It looks a bit much to anyone new to this, including myself, so let’s take it a section at a time. The first thing at the top, starting with “Shader,” is the Shader Block. This is used to define our Shader Object. You can use this to define your properties, create many shaders using the SubShader blocks, assign custom options, and assign a fallback shader object. Here you can see the name of our shader and that it is in the “Custom” directory.

Within the Shader block curly brackets, we have our other sections. The first is our Properties. The properties box is where we define the properties for our materials. A material property is what Unity stores along with our materials. This allows for different configurations within Unity by creating things like sliders and inputs within the Inspector window for us to play around with. We defined two properties, the MainColor and the MainTexture. Using square brackets, I outlined which property was the default color and default texture. We also defined the default values for these properties. There’s a bit to these values but suffice it to say, both values are default white.

The second block is our SubShader; this is where our shader logic goes. You can define multiple sub shaders for many different uses. For example, depending on the graphics hardware you want to support, you can make shaders for the various graphics APIs. Within our block, you can see some code for assigning tags, assigning levels of detail (LOD), and the CGPROGRAM block. I want to draw your attention to this section of the code:

Output function

First, we define the data types for our inputs and outputs and create a function for us to serve the outputs into unity. Our Input we set up as uv_Maintex; this allows for us to input a texture object. Then we create a fixed4 variable for our _Color attribute. The o.Albedo parameter is what is used to control the base color of the surface. Here we are taking the values of our texture and multiplying them by our color input. The code above gets you something similar to this:

Output function

I was proud of myself the first time I made this from memory. Our coded shader lets us control the color of the material and add basic textures to it. Working in graphics does not lead to instant gratification, as anything you do requires a ton of setup. However, this and ShaderToy get you that dopamine hit.

Conclusion

Above I went through some fundamentals of shaders in Unity. I skipped over a ton of information as I’m still learning a lot, and a full informed explainer would be twenty pages long. There is a lot to programming graphics and shaders specifically. I suggest you check out stuff like Team Dogpit’s shader tutorial for a way better deep dive. I’m excited to dig into this world. I want to learn to create some of the incredible stories I see in animation, and any first step is a step in the right direction. Thanks for reading.

-George