How does Unity save the normal vectors in a Normal Map? How do I get the normal vectors from a Normal Map texture in a shader?
Unity packs the normal vectors into Normal Map Texture2D assets using two encoding methods, depending on the platform.
For mobile platforms (or platforms that don’t support DXT compression), Unity saves the coordinates of the normal vectors normalized:
c[0] = 1.0f; // A = W
c[1] = (normal.x + 1.0F) * 0.5; // R = X
c[2] = (normal.y + 1.0F) * 0.5; // G = Y
c[3] = (normal.z + 1.0F) * 0.5; // B = Z
A normal vector of magnitude one can have negative and positive values, between -1 and 1. In the code above, the normal is being transformed into the 0 to 1 space.
The opposite transformation needs to be done in the shader, to decode the normal vector. Unity includes in the UnityCG.inccg file a helper function called UnpackNormal to perform this operation:
inline fixed3 UnpackNormal(fixed4 packednormal)
{
#if (defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)) && defined(SHADER_API_MOBILE)
return packednormal.xyz * 2 - 1;
#else
return UnpackNormalDXT5nm(packednormal);
#endif
}
In your shader code you would write something like this:
fixed3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
For other platforms, Unity will encode the NormalMap in the DXT5nm format. You can find information online about this format, for example here : http://tech-artists.org/wiki/Normal_map_compression. To summarize, DXT5 compression has more bits to store the information of the Green and Alpha channels. Therefore, it’s possible to store two of the components using those channels and calculate the third one.
c[0] = red; // A = W
c[1] = c[2] = c[3] = green; // RGB = XYZ
To obtain the normal vector from the NormalMap, you can also use UnpackNormal. The code for the DXT5nm specific unpacking is this:
inline fixed3 UnpackNormalDXT5nm (fixed4 packednormal)
{
fixed3 normal;
normal.xy = packednormal.wy * 2 - 1;
#if defined(SHADER_API_FLASH)
// Flash does not have efficient saturate(), and dot() seems to require an extra register.
normal.z = sqrt(1 - normal.x*normal.x - normal.y*normal.y);
#else
normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
#endif
return normal;
}
The UnpackNormal and UnpackNormalDXT5nm functions are found in UnityCG.cginc. That file is included with the Editor when installed, you can find it in the “CGIncludes” folder.
Alternatively you can download the shader source code from the Unity Download Archive : https://unity3d.com/unity/download/archive