Rainbow Shader (Screen Position as Texture Coordinates)

I wanted a ‘rainbow’ shader for Candy Bubble Drop – I have some text that will float up the screen when the player does something in the game that warrants a nice visual reward, and instead of plain white text, I thought it would be nice to have a multicolor text float up the screen and change color as it moves.

You can see the effect in the video below (all jerky right now – need to figure that out).  I’m just moving an NGUI UILabel around the screen after giving it my new material with the rainbow shader.

The shader grabs the screen position and uses that for the texture co-ordinates for each vertex. That way as the object moves, the texture changes – I thought it was pretty neat.  The shader code is below:

Shader "Custom/TexturedTextWithColor" {
   	Properties {
        _MainTex ("Font Texture (Alpha A)", 2D) = "white" {}
        _OverlayTex ("Overlay Texture (RGB)", 2D) = "white" {}
        _Color ("Text Color", Color) = (1,1,1,1)
        _WrapFactor ("Wrap Amount", float) = 1
    }

    SubShader {

		LOD 200
        Tags {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            //"ForceSupported" = "True"
        }
        Lighting Off Cull Off ZTest [unity_GUIZTestMode] ZWrite Off Fog { Mode Off }
        //Offset -1, -1
        Blend SrcAlpha OneMinusSrcAlpha

        Pass {	
            CGPROGRAM
// Upgrade NOTE: excluded shader from DX11 and Xbox360; has structs without semantics (struct v2f members scrPos)
#pragma exclude_renderers d3d11 xbox360
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata_t {
                float4 vertex : POSITION;
                fixed4 color : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f {
                float4 vertex : POSITION;
                fixed4 color : COLOR;
                float2 texcoord : TEXCOORD0;
                float4 scrPos;
            };

            sampler2D _MainTex;
            uniform float4 _MainTex_ST;
            uniform fixed4 _Color;
            sampler2D _OverlayTex;
            uniform float _WrapFactor;
            
            v2f vert (appdata_t v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.color = v.color * _Color;
                o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
                o.scrPos = ComputeScreenPos(o.vertex);
                return o;
            }

            half4 frag (v2f i) : COLOR
            {
                half4 col = i.color;
                float2 swap;
               
                swap.xy = _WrapFactor * i.scrPos.xy;
                swap.y += swap.x;

                col.rgb *= tex2D(_OverlayTex, swap).rgb;
                col.a *= tex2D(_MainTex, i.texcoord).a;
                //clip (col.a - 0.01);
                return col;
            }
            ENDCG 
        }
    }
}

The lines that make the ‘magic’ happen are:

     o.scrPos = ComputeScreenPos(o.vertex);

This code pulls the screen co-ordinates for the current vertex using a Unity3D supplied function.

The code in the fragment shader pulls those screen co-ordinates and then uses them to pull the texel from the Overlay Texture. I ended up swapping x & y to make the effect more pleasing when the text goes up the screen – I should have rotated the texture (which would be the more efficient solution). I set the alpha value based on the alpha from the font texture.

Leave a Reply