Normal mapping
Normal mapping, also known as “bump mapping”, is a texturing technique used for altering surface normals and allow for more complex interaction with lighting and other rendering features.
What are normals?
Fundamentally, normals are directions stored as unit-vectors ({x,y,z} coordinates that have a distance of 1 to the origin {0,0,0}). These are stored on vertices, triangles and/or triangle corners (also known in blender as a “split” or a “loop”) and specify which direction said element is pointing in.
You can visualize these in blender while in edit mode by enabling any of the "Normals" checks in the mesh edit overlay:

Example use case: Sunlight
Normals have a variety of use cases, and in the case of 3D rendering, one big use case is “Lighting”! The most simple form of lighting can be achieved using a “sunlight”: A light that, at its core, is just the direction in which the light “travels”.
To see how "shaded" a surface is, we check the angle*¹ between the surface normal and the sunlight direction:
- At 180° (“facing the sun”) the surface has a light influence of 1 and receives no shading.
- The closer we get to a 90° angle, the lower the light influence.
- At 90° we have a light influence of 0 and the surface is fully “shaded”.
- Angles between 90° and 0° have a negative light influence that gets rounded up to 0.

Manipulating normals using textures
This is where normal mapping comes into play: We can create an image where each pixel stores a normal that we can “add” to the normal on a surface! This way we can alter normals in between vertices, instead of just interpolating the normals between vertices.
A normal map uses each color channel to encode the components of a normal vector, where the value range from -1 to 1 gets remapped to the color channel range from 0 to 1 with the following mapping:
Channel | Axis | Direction (0->1) |
---|---|---|
Red | X+ | Left->Right |
Green | Y+ | Down->Up |
Blue | Z+ | Backward->Forward |
The default color, at which a normal map does not alter the surface normal, is #8080FF (the color code for {0,0,1}).

Blender’s standard normal map node requires the blue channel to be provided by the input, whereas Hedgehog Engine games don’t, as they derive the blue channel based on the red and green channels while rendering.
This works because, as mentioned above, a normal direction has to be a unit vector.
If the X and Y components sampled from a normal map are not a unit away from the origin, then the engine calculates a Z component that makes the normal a unit vector.

Formula for calculating the blue channel |
---|
B = sqrt(1 - ((R * 2 - 1)² + (G * 2 - 1)²)) * 0.5 + 0.5
R = Red |
Tangent Space
Normal maps come with a big issue: What is considered “up/down” and “left/right”? All we know without a doubt is that “forward/backward” is based on the normal that we want to add to, but how do we correctly apply the normal map?
This is where tangent space becomes relevant: The tangent of a normal points to the “right” of the normal (and the “binormal” would be perpendicular to the normal and tangent).
However, obtaining the tangent is a bit complicated. In the early days of normal maps, there was no consistent way to get a tangent, and there were many different ways to do so.
The naive answer would be to just take whatever direction “right” is in object space, which would work as long as all normal maps are facing perfectly upwards, but the moment a texture gets slightly rotated, the lighting looks very off.

Fortunately nowadays most 3D software (including blender and Hedgehog Engine 2) uses “MikkTSpace”, which was introduced in 2011 and bases the tangent off the texture coordinates, which also automatically aligns the tangent with how a standard normal map would be used.
The easiest way to visualize MikkTSpace (Short for "Mikk tangent space") is by using a texture that shows “up” and “right” and displaying those via a material:

Older games may have used normals that depended on the non-standard tangents, and can thus look weird after importing. Unfortunately, blender supports no way of importing tangents or modifying them beyond being calculated.
Creating normal maps
There are 2 primary ways by which normal maps are created:
Height mapping
Height maps are exactly what the name implies: a texture depicting a height. We can use such a height map to generate a normal texture:

There exist many tools to create normal maps from height maps, such as
You can even use heightmaps in blender directly using the Bump node.
Baking
Blender and other 3D software provide a feature called “texture baking” with which you can project normals from a more detailed mesh onto a simpler one and save it to an image.
You can read more about texture baking in blender here.
