We wanted a hologram effect for a cube character. The effect combines

  • Glowing face

  • Glitching body

  • Varying color at edges (fresnel effect)

  • Transparent fade at bottom

  • Animated scan lines

  • Outlines around main body

The shader for the body uses hard-coded vertex values and does not use textures (by design). A nice general purpose hologram shader with more features can be found on the [Unity Asset Store by Zololgo](https://assetstore.unity.com/packages/vfx/shaders/sci-fi-hologram-shader-66455).

<video muted autoplay controls> <source src="{{site.media_location}}/BurpleHologram.mp4" type="video/mp4"> </video>

Face shader

This shader blurs a texture’s green channel and adds it to the original texture. This shader:

  • creates a green glow around the whites of the eyes

  • turns the rosy cheeks green

![](/media/BurpleHologram-Face1.png) | ![](/media/BurpleHologram-Face6.png) | size = 1 (box size = 3) | size = 6 (box size = 13) |

Shader "Hologram/HologramFaceShader"
{
  Properties
  {
    _MainTex ("MainTexture", 2D) = "white" {}
    _Size ("Blur size", Range(1,20)) = 2.0
  }
  SubShader
  {
    Tags { "Queue"="Transparent" "RenderType"="Transparent" }
    Blend SrcAlpha OneMinusSrcAlpha
    LOD 100
    ColorMask RGB
    Cull Back

    Pass
    {
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag

      #include "UnityCG.cginc"

      struct appdata
      {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
        float2 uv : TEXCOORD0;
      };

      struct v2f
      {
        float4 vertex : SV_POSITION;
        float2 uv : TEXCOORD0;
      };

      sampler2D _MainTex;
      float4 _MainTex_ST;
      float4 _MainTex_TexelSize;
      float _Size = 2.0;

      v2f vert (appdata v)
      {
        v2f o;

        o.vertex = UnityObjectToClipPos(v.vertex);
        o.uv = TRANSFORM_TEX(v.uv, _MainTex);
        return o;
      }

      fixed4 frag (v2f i) : SV_Target
      {
        fixed4 texColor = tex2D(_MainTex, i.uv);

        // Box blur linear filter on green channel
        float start = -0.5 - _Size;
        float end = -start + 0.1; // _Size + 0.5 + epsilon

        float green = 0.0;
        for (float uu = start; uu < end; uu++)
        {
          for (float vv = start; vv < end; vv++)
          {
            // Add green channel
            fixed3 offset = fixed3(vv, uu, 0.0) * _MainTex_TexelSize.xyz;
            green += tex2D(_MainTex, i.uv+offset).g;
          }
        }

        // Add average green value for green glow
        float size = 2*_Size+1;
        texColor.g += green/(size*size);

        return texColor;
      }
      ENDCG
    }
  }
}

Body Shader

This shader distorts the geometry of the cube in the vertex shader and colors the cube in the fragment shader. The outline is implemented based on the distance of pixels to the edges of the cube. The cube’s dimensions are hard-coded. This approach is a hack which makes it easy to visualize the edges of the cube without using edge detection or extruding along normals. But note that this approach only works because the body is not animated!

![Burple Hologram Settings](/media/BurpleHologram.png)

Shader "Hologram/HologramShader"
{
  Properties
  {
    _MainColor ("MainColor", Color) = (1,1,1,1)
    // Rim/Fresnel
    _RimColor ("Rim Color", Color) = (1,1,1,1)
    _RimPower ("Rim Power", Range(0.1, 10)) = 5.0
    // Scanline
    _ScanTiling ("Scan Tiling", Range(0.01, 100.0)) = 0.05
    _ScanSpeed ("Scan Speed", Range(-10.0, 10.0)) = 1.0
    _ScanMag ("Scan Darkness", Range(0.0, 1.0)) = 0.4
    // Outline
    _OutlineColor ("Outline Color", Color) = (0,0,0,1)
    _OutlineWidth ("Outline Width", Range(0.001, 1.0)) = 0.02
    // Glitch
    _GlitchSpeed ("Glitch Speed", Range(0, 50)) = 1.0
    _GlitchIntensity ("Glitch Intensity", Float) = 0.1
  }
  SubShader
  {
    Tags { "Queue"="Transparent" "RenderType"="Transparent" }
    Blend SrcAlpha OneMinusSrcAlpha
    LOD 100
    ColorMask RGB
        Cull Back

    Pass
    {
      CGPROGRAM
      #pragma shader_feature _SCAN_ON true
       #pragma shader_feature _OUTLINE_ON true
      #pragma shader_feature _GLITCH_ON true
      #pragma vertex vert
      #pragma fragment frag


      #include "UnityCG.cginc"

      struct appdata
      {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
      };

      struct v2f
      {
        float4 vertex : SV_POSITION;
        float3 viewDir : TEXCOORD1;
        float3 localVertex : TEXCOORD2;
        float3 worldNormal : NORMAL;
      };

      float4 _MainColor;
      float4 _RimColor;
      float _RimPower;
      float4 _OutlineColor;
      float _OutlineWidth;
      float _ScanTiling;
      float _ScanSpeed;
      float _ScanMag;
      float _GlitchSpeed;
      float _GlitchIntensity;

      v2f vert (appdata v)
      {
        v2f o;

        // Glitches
        v.vertex.z += _GlitchIntensity * (step(0.5, sin(_Time.y * 2.0 + v.vertex.y * 1.0)) * step(0.99, sin(_Time.y*_GlitchSpeed * 0.5)));

        o.localVertex = v.vertex;
        o.vertex = UnityObjectToClipPos(v.vertex);

        fixed4 worldVertex = mul(unity_ObjectToWorld, v.vertex);
        o.worldNormal = UnityObjectToWorldNormal(v.normal);
        o.viewDir = normalize(UnityWorldSpaceViewDir(worldVertex.xyz));

        return o;
      }


      fixed4 frag (v2f i) : SV_Target
      {
        // Scanlines
        float scan = (1.0+sin(cos(i.localVertex.y) * _ScanTiling + _Time.w * _ScanSpeed)) / 2.0;
        scan = scan * (_ScanMag) + (1-_ScanMag);

        // Rim colors around side and top
        half rim = 1.0-saturate(dot(i.viewDir, i.worldNormal));
        fixed4 rimColor = _RimColor * clamp(pow (rim, _RimPower), 0.0, 1.0);

        float v = 0.0;
        float w = _OutlineWidth;
        v = max(v, smoothstep(w+0.01, w, distance(i.localVertex.xy, fixed2( 1.0, 3.7))));
        v = max(v, smoothstep(w+0.01, w, distance(i.localVertex.xy, fixed2(-1.0, 3.7))));
        v = max(v, smoothstep(w+0.01, w, distance(i.localVertex.zy, fixed2( 1.0, 3.7))));
        v = max(v, smoothstep(w+0.01, w, distance(i.localVertex.zy, fixed2(-1.0, 3.7))));

        float s2 = sqrt(2)/2.0;
        fixed2 dir = normalize(i.localVertex.xz);
        v = max(v, smoothstep(w+0.01, w, abs(dot(dir, fixed2(s2, s2)))));
        v = max(v, smoothstep(w+0.01, w, abs(dot(dir, fixed2(-s2, s2)))));

        fixed4 col = _MainColor * scan * (1-rim) + rimColor*rim + v * _OutlineColor;

        // Alpha based on y
        float alpha = clamp(pow(i.localVertex.y/3.0,2.0), 0.0, 1.0);
        col.a = alpha;

        return col;
      }
      ENDCG
    }
  }
}