So a while ago my friend was talking about having highlights on the player for the game InTheShadows. At first I thought it would be too much work, working in Unity 4x, there was no shader available to do this easily. I've never worked on shaders before and didnt know where to begin so I researched online and put together some bits of code to make a single shader that had all the features I needed.
Without normal map / With normal map
So turns out adding normal map was pretty easy, including the process of generating the maps and adding the effect without having to mess with my animation system or any actual game code, beside shaders.
So lets start with generating the normal map. For that I use Nvidia normal map filter tool for Photoshop. If you are not using Photoshop, there is a plug-in equivalent available for gimp. Its free and easy to use. At the resolution my sprite are it does make some error that need to be manually corected but otherwise it does a pretty good job
Filtered single sprite
This is the sprite shader. You can copy and paste it in an empty file and add the extention .shader. It has pixel snap, normal mapping and normal mapping intensity control. I can't take credit for most of it, I wish I remember where I found all those lines of code. This is a prototype and need ajustement but it does what it need to be doing right now.
Shader "Sprites/Bumped Diffuse with Shadows"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_BumpMap ("Normalmap", 2D) = "bump" {}
_BumpIntensity ("NormalMap Intensity", Range (-1, 2)) = 1
_BumpIntensity ("NormalMap Intensity", Float) = 1
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
_Cutoff ("Alpha Cutoff", Range (0,1)) = 0.5
}
SubShader
{
Tags
{
"Queue"="AlphaTest"
"IgnoreProjector"="True"
"RenderType"="TransparentCutOut"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
LOD 300
Cull Off
Lighting On
ZWrite On
Fog { Mode Off }
CGPROGRAM
#pragma target 3.0
#pragma surface surf Lambert alpha vertex:vert alphatest:_Cutoff fullforwardshadows
#pragma multi_compile DUMMY PIXELSNAP_ON
#pragma exclude_renderers flash
sampler2D _MainTex;
sampler2D _BumpMap;
fixed _BumpIntensity;
fixed4 _Color;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
fixed4 color;
};
void vert (inout appdata_full v, out Input o)
{
#if defined(PIXELSNAP_ON) && !defined(SHADER_API_FLASH)
v.vertex = UnityPixelSnap (v.vertex);
#endif
v.normal = float3(0,0,-1);
v.tangent = float4(1, 0, 0, 1);
UNITY_INITIALIZE_OUTPUT(Input, o);
o.color = _Color;
}
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * IN.color;
o.Albedo = c.rgb;
o.Alpha = c.a;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
_BumpIntensity = 1 / _BumpIntensity;
o.Normal.z = o.Normal.z * _BumpIntensity;
o.Normal = normalize((half3)o.Normal);
}
ENDCG
}
Fallback "Transparent/Cutout/Diffuse"
}
At first it didnt have the intensity slider, but it soon became apparent that in most case during game play there was just too much highlights going on. I wanted to have a relatively flat look in normal circumstance, exept when the player is near a light source other than the sun.
To fix the intensity, I thought about having "intensity zones" using a gameobject with a gizmo. I can place intensity zones and control the intensity in the scenes. Making the code execute in edit mode makes it easier to tweak since I don't have to run the game to fine tune the affected spots.
As you can see, the light cube has a light, but there is also a gizmo (wire sphere) that represent the area of intensity for the normal map on the player. On the first image there is visually no normal map, has soon as the player enter the sphere the normal map start raising proportionally to the distance of the center of the sphere.
This technique works pretty well I think, and could be used in other situation. It also works with unity Mecanim system.
I think this shader could be useful for others beginner unity devs like me, so feel free to use it!