I started experimenting today with some ideas that I knew how to achieve with node based shader editors. I wanted to see how my knowledge of them transferred over into code and it seemed to be pretty well.

Vertex Offset and Sine Wave

I added a vertex offset to the shader, which moves up and down using the value of a sine wave based on time. It’s actually very easy to add, as its from a library of variables automatically included into your shaderlab code. You can read more about it here:

https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html

Sine Wave

This is how a sine wave looks like over time. So we can use it for an effect that looks a bit like the mesh is floating/hovering up and down.

The variable we need is _SinTime, it’s a float4, and the channels are as follows:

_SinTime.x = time/8
_SinTime.y = time/4
_SinTime.z = time/2
_SinTime.w = time

So all we have to do is put it into the rest of the code however we like. I personally multiplied it by a value property I added as a slider, and then added this to the vertex positions.

Shader "Ryans/ZoffsetNormal"
{
    Properties
    {
        // _VariableName ("editor name", Range (min, max)) = default
        // This is a slider in the material editor
        _ZOffset ("Z Offset", Range (0.0,1.0)) = 0.0
...

The slider will then appear right there in the material editor with the name and value range that you gave it.

You can then do your calculation for the vertex position like so:

...
            // Vertex Shader
            vertexOutput vert (vertexInput v)
            {
                vertexOutput o;

                // Add the offset calculation when we convert to ClipPos
                o.vertex = UnityObjectToClipPos(v.vertex + float3(0, _ZOffset*_SinTime.w, 0));
...

And that’s all there is to it! Pretty simple right?

If your model does not seem to be moving, or only moves when you move the camera in the scene view for example, you need to make sure you have “Animated Materials” ticked, at the top of the window.

Fresnel

A Fresnel shader is useful for making things like rim lighting, or hologram-looking materials for instance. It is calculated from the dot product of the view direction and the direction of the model’s normals.

If you need help understanding what a dot product is, you can check out this gif from Freya Holmer on Twitter. She also posts a lot of helpful shader related tips over there so shes definitely someone you should follow!

For this shader I added a float property “Power” to the inspector which will be used later inside a pow function. Again, this will be a slider with a min-max value like the shader from before.

Shader "Ryans/Fresnel"
{
    Properties
    {
        _Pow ("Power", Range (0.0,5.0)) = 1.0
    }
...

In the struct vertexOutput we need to add a float3 of the view direction. We also have to make sure to declare the float variable _Pow that we made earlier show up in the inspector, so that we can use it in the fragment shader.

Inside the vertex shader, we can use the float3 ObjSpaceViewDir (float4 v.vertex) since we have #include UnityCG.cginc earlier on in the shader code. This gives us the object space direction from the vertex position, towards the camera. It is not normalized, so we have to put it inside a normalize(v) to keep the same vector direction, but set the length to 1.

Inside the fragment shader we can now have the viewDir which is sent from the vertex shader, and use it inside our dot product with our vertex normal. The code dot(o.viewDir, o.normal) does exactly this, but we want to invert it, so we do a 1 -, then put that into the pow with our Power value from the inspector as the exponent, and saturate it to clamp it the final value between 0 and 1.

In steps its like so:

  1. Calculate dot product of view direction and vertex normal
  2. Subtract the above value from 1 to invert it
  3. Adjust the values with the power function using our power exponent variable
  4. Saturate the value to clamp it between 0 and 1

It might sound like a lot, but you can build up interesting effects by using all of these functions and math together!

...
            struct vertexOutput
            {
                float4 vertex : SV_POSITION;
                float3 normal : NORMAL;
                float3 viewDir : TEXCOORD1;
            };

            // Variables
            float _Pow;

            // Vertex Shader
            vertexOutput vert (vertexInput v)
            {
                vertexOutput o;

                o.vertex = UnityObjectToClipPos(v.vertex);

                // We need to normalize our view direction in object space, per vertex
                o.viewDir = normalize(ObjSpaceViewDir(v.vertex));
                o.normal = v.normal;

                return o;
            }

            // Fragment shader
            fixed4 frag (vertexOutput o) : SV_Target
            {
                // Saturate clamps the value between 0 - 1
                fixed4 colouroutput = saturate (pow (1 - ((dot(o.viewDir, o.normal))), _Pow));
                
                return (colouroutput);
            }
            ENDCG
...

Adjusting the Power exponent value in the inspector is similar to moving the mid point of an image’s ‘levels’ inside Photoshop for example.