Recently while working with normal maps i discovered something interesting:

Normal map that I baked using Blender cycles: enter image description here

Length of the normal vectors contained in the normal map: enter image description here

Length of the normal vectors contained in the normal map divided by 2: enter image description here

As it happens, the normal vectors in normal maps are not normalized but instead have a specific length depending on which direction it is facing.

I do have vague knowledge of how normal maps roughly work, e.g. RGB represent XYZ coordinates with 0.5;0.5;1 being neutral, but apart from that I don't know anything about the actual calculations of when the normal map is created and used.

Can somebody please explain how tangent space normals are calculated?

Why are the resulting normal vectors not normalized?

  • $\begingroup$ What exactly is shown in the second two pictures? $\endgroup$
    – scurest
    May 14, 2022 at 19:39
  • $\begingroup$ Btw, source code for normal mapping (easy to read). $\endgroup$
    – scurest
    May 14, 2022 at 19:41

1 Answer 1


Edit: The answer turns out to be a combination of 3 and 5 below. (5 was added in this edit.) As made plain by excellent conversation with @scurest, Blender does not orthonormalize its tangent space, and we shouldn't expect unit vectors out of its normal map bakes. They are often very close to unit vectors; it depends on the rate of change of the normal across the face. Blender's treatment of this is not by-the-book, and creates problems for which I've submitted a bug report, but it is not unique either (I tested on xnormal, and it doesn't use orthonormal tangent space either; I've posted some questions regarding how other software handles it, but have no replies as of yet, and it's esoteric enough that basic internet research isn't any use.)

Let's try it out. We'll bake a simple normal map, from a cube to an enclosing UV sphere, and see what our normals look like:

enter image description here

To a reasonable precision, it looks good. None of my normals are shorter than 0.99 and none of them are longer than 1.01. We shouldn't expect much more precision from 8 bit-per-channel color than that.

This image was generated with a single sample bake, but repeating the bake with 64 samples shows no difference.

So what am I doing different from you? I can't say, because I don't have access to all of the details of your bake or your analysis material. Here are some potential differences:

  1. You weren't doing a selected to active bake, but instead baking from normals on a single object, which themselves weren't normalized.

  2. You weren't baking and reading your normal map as non-color data.

  3. You weren't remapping your normal map from the 0,1 range (which is necessary for almost all image formats) to the -1,1 range (which is the space necessary to represent the full range of normalized vectors.)

  4. You were using texture filtering, by using "Linear" instead of "Closest" in your image texture node, which means that the actual color you were reading from the normal map wasn't a single texel, but was instead a box-blurred combination of several texels.

  5. You're using different meshes, and something about the meshes I chose happens to minimize the issue.

  • $\begingroup$ You can't in general expect the RGB values to be a unit length vector (after remapping) though, that only happens if the (N, T, B) vectors are orthonormal. That should be true at the corners of a tri, but in the middle when interpolating it isn't guaranteed anymore. That's also why the normal mapping node normalizes its output. $\endgroup$
    – scurest
    May 15, 2022 at 1:18
  • 1
    $\begingroup$ It does, but It it only computes them at poly corners. On the poly interior, the normal and tangent are linearly interpolated from the corners, just like UVs, vertex colors, etc. and there's no guarantee they will be orthonormal just because they were at the corners. (B is computed at each point from the cross product of the interpolated N and T.) $\endgroup$
    – scurest
    May 15, 2022 at 1:35
  • 1
    $\begingroup$ The interpolated N and T aren't normalized (so B isn't either), AFAICT. There's no reason to since it normalizes after linearly combining them. Why is that not smart, how else would you do it? I don't know how the baking works. My whole understanding is based off the source code I linked above. $\endgroup$
    – scurest
    May 15, 2022 at 3:28
  • 2
    $\begingroup$ @scurest I tested, then made a bug report at developer.blender.org/T98175 . I realized orthogonality is important for the same reason: constant shear is not a problem, but shear that varies with deformation is (and deformation is why we use tangent space.) Thank you very much for your conversation; without it, I probably would never have developed my thoughts beyond "I've got a bad feeling about this Chewie." $\endgroup$
    – Nathan
    May 16, 2022 at 22:17
  • 1
    $\begingroup$ Yup, that's right. Although be aware that "tangent space" is something different in different applications; the bug report I mentioned in my comment to scurest might make it more clear how different applications can calculate tangent space differently. Blender's tangent space does not necessarily have unit length (or uniformly scaled) basis vectors, so if you normalize(1,0,1) you're not usually going to end up with a 45 degree angle from the normal. $\endgroup$
    – Nathan
    May 17, 2022 at 17:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .