When working on my 2D lighting system, I needed to create some shadows. Shadows for sprites can easily be created by skewing the sprite and drawing it in all black. How much you skew depends on the position in relation to the light source. I already had a lighting and shadow system working in XNA, and now I just needed to get it working in Unity.
My first thought was to perfrom a skew from within Unity, in C#, like I did in XNA. As it turns out, you can’t (you can skew GUI elements, but not actual game objects). If you want to skew game objects, you need to skew the mesh itself, which I did actually get to work. However, this solution was a little ugly, and I’m not sure about the performance ramifications when recalculating every frame. So, I set off to find a more elegant solution.
My next idea was to try to skew the sprite from within the vertex shader. I knew it had to be possible, but I wasn’t sure exactly how. To my great surprise (and frustration), I was absolutely unable to find a simple vertex shader skew demo or tutorial anywhere on the web. I searched for literally hours one day, but came up empty. I suppose I shouldn’t be too surprised, since vertex shaders are rarely used with pure 2D games.
With no options left, I had a go at it myself. After a bit of trial and error I was able to create a working solution. All in all, it actually took less time for me to implement it myself than I spent searching for a tutorial! Sigh. Live and learn.
I’ll share the shader here in hopes of saving someone the hassle of what I went through. For a great article on understanding skew matrices themselves, check out this Flash transformation matrix tutorial. Enjoy!
- // Adapted from the built-in Sprites-Default.shader
- // You can download the source for the built-in shaders from:
- // http://unity3d.com/unity/download/archive
- Shader "Sector12/SpriteSkewShader"
- {
- Properties
- {
- [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
- _Color ("Tint", Color) = (1,1,1,1)
- [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
- _HorizontalSkew ("Horizontal Skew", Float) = 0
- _VerticalSkew ("Vertical Skew", Float) = 0
- }
- SubShader
- {
- Tags
- {
- "Queue"="Transparent"
- "IgnoreProjector"="True"
- "RenderType"="Transparent"
- "PreviewType"="Plane"
- "CanUseSpriteAtlas"="True"
- }
- Cull Off
- Lighting Off
- ZWrite Off
- Fog { Mode Off }
- Blend One OneMinusSrcAlpha
- Pass
- {
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #pragma multi_compile DUMMY PIXELSNAP_ON
- #include "UnityCG.cginc"
- struct appdata_t
- {
- float4 vertex : POSITION;
- float4 color : COLOR;
- float2 texcoord : TEXCOORD0;
- };
- struct v2f
- {
- float4 vertex : SV_POSITION;
- fixed4 color : COLOR;
- half2 texcoord : TEXCOORD0;
- };
- sampler2D _MainTex;
- fixed4 _Color;
- float _HorizontalSkew;
- float _VerticalSkew;
- v2f vert(appdata_t IN)
- {
- v2f OUT;
- OUT.texcoord = IN.texcoord;
- OUT.color = IN.color * _Color;
- // Create a skew transformation matrix
- float h = _HorizontalSkew;
- float v = _VerticalSkew;
- float4x4 transformMatrix = float4x4(
- 1,h,0,0,
- v,1,0,0,
- 0,0,1,0,
- 0,0,0,1);
- float4 skewedVertex = mul(transformMatrix, IN.vertex);
- OUT.vertex = mul(UNITY_MATRIX_MVP, skewedVertex);
- #ifdef PIXELSNAP_ON
- OUT.vertex = UnityPixelSnap (OUT.vertex);
- #endif
- return OUT;
- }
- fixed4 frag(v2f IN) : SV_Target
- {
- fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;
- c.rgb *= c.a;
- return c;
- }
- ENDCG
- }
- }
- }