Making opaque shaders is fun and all… but transparent shaders are fun too!

You can use transparency for an awful lot of special and cool effects so it’s definitely worth learning how to add it to a shader.

I was looking up the documentation for this but struggled a little to understand everything. While I was searching I found a video from the learn.Unity page with a video about just what I was looking for!

It seems there’s earlier videos in that series too which seem like a good opportunity for learning about basic shader development too!

Vertex Colour & Transparency

For this shader I thought about also learning how to include the colour data from the vertices too. Vertex colours are incredibly powerful in shaders because you can use them as masks for different effects and calculations. In mobile development it means you can get even more data out of your meshes without the need of expensive textures.

Getting the Vertex Colours

Getting the vertex colour data is actually pretty simple, we just need to make sure to add in our vertexInput and vertexOutput structs, float4 color : COLOR; and then we can use it later simply where needed.

...
            struct vertexInput
            {
                float4 vertex : POSITION;
                float4 color : COLOR;
            };

            struct vertexOutput
            {
                float4 vertex : SV_POSITION;
                float4 color : COLOR;
            };
...

Adding Transparency

For the transparency we have to add a couple more things, and alter the Tags inside the subshader. Tags allow you to define the "Queue" and "RenderType" amongst other things.

Queue – The shaders position in the queue for rendering order. "Transparent" objects need to render after opaque objects so that we can see through them.

More information about the render queue can be found here:
https://docs.unity3d.com/ScriptReference/Rendering.RenderQueue.html

RenderType – The shaders category, from a set of predefined groups. Used for producing the cameras depth texture and for shader replacements.

More information about RenderType categories can be found here:
https://docs.unity3d.com/Manual/SL-SubShaderTags.html

We also need to turn off rendering the pixels to the depth buffer, as we want to use transparency. For this all you need to do is write ZWrite off below the Tags we added before.

More information about ZWrite and culling can be found here:
https://docs.unity3d.com/Manual/SL-CullAndDepth.html

Blend Modes

After that we need to define which blend mode we want to use. for traditional transparency, we need to add Blend SrcAlpha OneMinusSrcAlpha. You can also look at the documentation to see how to do things like additive or multiplicative blending.

There’s also a BlendOp blend operation which you can choose too, including some which you might be used to from image-editing software like Add, Subtract, Min, Max etc. However I am yet to use and fully understand these.

More information about blending, and the most common types can be found here:
https://docs.unity3d.com/Manual/SL-Blend.html

Finishing Up

We have to define our _Alpha property as a variable to use it as the w/a channel of the float4 colour which we return from the fragment shader at the end. The vertex colours can be passed from vertex shader to fragment shader to use as our final colours along with the alpha value.

Shader "Ryans/VertexColourAlpha"
{
    Properties
    {
        _Alpha ("Alpha", Range (0,1)) = 0.5
    }
    SubShader
    {
        // Render transparent, put it in the queue to render after opaque
        Tags { "Queue"="Transparent" "RenderType"="Transparent" }
        LOD 200

        // Dont write to depth buffer as its transparent
        ZWrite off

        // Blend Factor, this is for traditional transparency blending
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct vertexInput
            {
                float4 vertex : POSITION;
                // Use this naming for vertex colours
                float4 color : COLOR;
            };

            struct vertexOutput
            {
                float4 vertex : SV_POSITION;
                float4 color : COLOR;
            };
            
            float _Alpha;

            vertexOutput vert (vertexInput v)
            {
                vertexOutput o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.color = v.color;

                return o;
            }

            fixed4 frag (vertexOutput o) : SV_Target
            {
                // Use the _Alpha material parameter as the alpha
                o.color.a = _Alpha;
                return float4(o.color);
            }
            ENDCG
        }
    }
}

And there we have it, simple transparency! We could still push this further and hide the overlapping geometry that is still rendered, perhaps in the next post I will cover that!

We also might need to convert the sRGB vertex colours to linear space too.