I am learning about normal mapping. I understood that RGB values are converted into XYZ, but my question is how is it converted and why is the normal map blue and purple in color?
Only tangent space normal maps are primarily blue. This is because the colour blue represents a normal of (0,0,1) which would be an unchanged normal when the triangle lies in the plane x and y, i.e. perpendicular to the surface. The tangent, x and bi-tangent, y (also referred to as bi-normal) are encoded in the red and green channels and these form to create the a tangent space normal for a point on the surface of the triangle.
If a tangent space normal map was to encode a colour only in red (1.0, 0.0, 0.0) this would generate a tangent-space normal parallel to the triangle surface. This is never seen because it would mean that the triangle would only ever be lit at 90degrees from the surface and view vector at which point you wouldn't be able to see the triangle anyway.
World space normal maps encode the unit normal over a sphere so can be primarily different colours once encoded from [-1, 1] to [0, 1] per channel.
A comparison can be seen here:
In practise normal maps are usually encoded in a 2 channel format such as BC5 which actually only stores the x and y with the z being reconstructed as we know it's a unit vector. This allows you to maintain higher precision with more bits without increasing the file size.
$\begingroup$ Tangent spaces are not uniquely parametrized, yet I find a lot of mesh+normal-map-in-tangent-space images around. Is there a conventional way to define the tangent space basis? $\endgroup$ May 29, 2019 at 15:34
Because a normal map is covering vectors from -1 to 1 it makes sense to stretch this range into 0->1 so all of it can be fit inside the range of RGB.
So usually we apply a transform on the normal to convert it into something we can see.
vec3 colour = vec3(0.5) + normal * 0.5;
The blue colour is because normal maps are supposed to be used relative to the primitive (triangle/etc) normal with a default (0,0,1) direction to indicate no deviation from the triangle normal. Usually the fragment shader needs to rotate this normal in relation to the current per-pixel normal at runtime.
$\begingroup$ Is this code
vec3 colour = vec3(0.5) + normal * 0.5;is converting RGB to XYZ ? $\endgroup$ Sep 13, 2018 at 13:09
1$\begingroup$ @AbhinaySInghNegi no, colour is RGB and normal is XYZ, so this is converting XYZ to RGB. If you want RGB to XYZ you use
vec3 normal = (colour - vec3(0.5)) * 2.0;$\endgroup$– bram0101Sep 13, 2018 at 15:55