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
}
}
}