Baking Normals, Tangents and Binormals from Blender3D

Intro

If you don't know what these terms mean, you probably followed an evil link into this page; but in case you don't but wish to know, basically, normals are vectors perpendicular to a mesh surface. You might be familiar with those sky-bluish textures called “normalmaps”… This is NOT it, though it is closely related: Normalmaps are NOT normals; they are normal modifiers; and are usually encoded in tangent space. That's why they look mostly bluish, with occasional ripples of other colors.
When we're talking about “baking normals” here, we're talking about baking the un-modified, vertex normals, extrapolated across the texture between vertices, and in a “model-space” coordinate system.
Thus, if a surface is front-facing (positive Z axis) the normal vector will point forward into the Z axis. That is, its value will be (0.0, 0.0, 1.0) in xyz, and its baked color will be 0.5 red, 0.5 green, 1.0 blue, –a sort of “sky-blue”. Why 0.5 red and green? Because when colors are used to encode vectors, usually a 0.0 color value represents a negative one quantity; so zero is expressed as a half-tone.

And, what about Tangent and Binormal?

First, let me clarify that the name “binormal” is ugly, and rightfully controversial in this context. Having said that, the Tangent and Binormal are two other vectors, both normal (right-angle) to the normal (and therefore tangential to the mesh surface, at any given point); and at right angles to each other –at least in theory. Thus, the normal, tangent and binormal are 3 mutually-perpendicular vectors that define a coordinate system rotation relative to another, or a rotation matrix.
But there can be infinite rotation matrices defined around a given normal, so which particular way do the tangent and binormal point? They point in ways that spacify how the texture mapping (UV mapping) is rotated at a given point:

* The tangent points in the 3D model space direction that, on the texture, would point to the right of the screen.
* The “binormal” points in the 3D model space direction that, on the texture, would point up on the screen.

What other space is there, besides model-space? In 3D graphics there are many spaces commonly used, such as world space, object space, camera space…; but the one other space closely related to these vectors is “tangent space”, A.K.A. “texture space”, where x and y in the texture (u and v, rather) are the x and y of tangent space; but the z of tangent space is a direction off the texture, –towards you, if you're looking at it straight.
Note that tangent space is not a constant rotation of model space; the rotation matrix changes at each point on the surface of the mesh; sometimes non-continuously.

So, the normal, tangent and binormal, in fact, together define a matrix of rotation that can be used to translate vectors in model space to tangent space. Three textures times 3 channels = 9 values per texel, them being the 9 values for the rotation matrix at that point.

Why do we need these darn bakes?

Because many of our self-shadowing computations are baked and computed in model-space, but eventually need to be converted to tangent space

How do we, then?

What we need

To bake normals, tangents and binormals, you'll need some auxiliary textures… Small textures, even a single texel would do, as all we need from them is a solid color; but I make them 16 x 16, or 32 x 32, just in case some part of the software might have subtle bugs associated with smaller textures than the coders anticipated (call me paranoid). This color is actually a vector, encoded in RGB representing XYZ, where 1.0 color represents 1.0 coordinate position; 0.5 represents 0.0; and 0.0 represents -1.0 (negative).

1.0, 0.5, 0.5 pink will help us bake tangents

0.5, 1.0, 0.5 green will help us bake binormals

0.5, 0.5, 1.0 blue will help us bake normals (this texture is not necessary, in the case of normals, but just for consistency of methodology)

This is what they look like:

But, Houston, we have a problem: PNG colors are 8-bits precision per channel. The maximum brightness for any given channel is binary 11111111, hex FF, or decimal 255, which encodes 0.9960937 (255/256), rather than 1.0; and 0.5 is not precisely representable (nearest values are 0x7F and 0×80). The error is NOT small enough to disregard or ignore. Fortunately, Blender can read texture formats of higher precision. Rather than PNG's, I use 32-bit EXR's.
For your convenience, I've provided the EXR versions of the images. Use them, instead of the PNG's:

pink0001.exr
green0001.exr
blue0001.exr

Download them into your Blender file's folder, or working folder of your choice.

We also need a Blender file, of course, the model, which has to have a (non-overlapping) UV mapping; –otherwise the bakes would be meaningless.
To begin, open up the file, set-up a UV edit window; back in the 3D edit window go to Edit mode, select All; then in the UV window, Image menu, load a texture of the size you want your bakes to be (the texture should show on the model if you are in textured view mode; I'm using a baked ambient occlusion, but any texture, even blank, will do); then hit F9 and click in the New (material) button:


Setting up the material

Hit F5, select NEW MATERIAL, name it something appropriate, such as “tanbinormbake”. We will assign all 3 textures to this material and then enable one at a time for the three bakes.
I'm not sure if these texture settings make any difference, but just in case… I make my texture black, shadeless, no shadows, no bias, no ambient, no nothing… as per the image below:

Set up three texture handles on the textures panel; name them “blue”, “green” and “pink”.

Now, for all three image handles (select one at a time, then…)
* Go to the Map Input tab, and click on UV

* Then go to the Map Output tab, click off the Col button, and click instead the Nor button TWICE; –the second time you click it it turns yellow, meaning that its value is inverted. Mode should be “Mix”. Turn sliders Col, Var and Disp to zero (all the way to the left) and the Nor slider to 1.0 (one). Well, you'd never get exactly 1.0 using the slider; you have to type the number in.

Repeat the above steps (Map Input and Map Output tab settings) for all three image handles.

Before we forget, hit F9 and Assign this material to the entire mesh.

Of course, if you are using all kinds of materials with your mesh already, it would be a bummer to overwrite all material assignments for the sake of a bake. What you can do, though, is NOT save the .blend file, or to save it under a different name.

Now we're going to load the textures. Hit F5 to get back to the materials panel, then click on the leopard skin button:

The blue, green and pink texture handlers will appear on the main list already. Select one at a time (you'll have to repeat the following steps for each of the three handlers): In Texture Type select Image:

In Map Image, you want to set Normal Map, and the mode to Tangent; and you can remove any mipmapping and filter settings; we don't need filtering.

Finally, load the image. For the “blue” handler, we load blue0001.exr. I always hit the Relative Path button in the file load screen.

Repeat the above steps for the green handler…

…and for the pink handler:

Doing the bakes

Go back to the materials panel (F5) and de-select all texture handlers but one (the first, blue):

Hit F10 to go to the render panel, click on Normals, and set Margin to the maximum (32); then hit BAKE:

You should get a colorful result. :)

img16.jpg

If the texture selected is the blue one, what we've just baked is the normals. To save the bake, in the UV Edit Windo, Image menu, hit SaveAs…
In the file browser window that opens up, set the texture type to OpenEXR, click on Relative Path, and enter “normals” for the name.
NOTE: This will save the baked image as “normals.EXR”, a high precision image format for use with Blender noodles, but one format that will NOT open for viewing in Gimp or other standard image programs. If you want to view the image in Gimp, save it again, this time specifying PNG format.

F5, de-select blue handler, select green handler, F10, BAKE…

img18.jpg

…Save As “binormals.EXR”.

F5, de-select green handler, select pink handler, F10, BAKE…

img19.jpg

…Save As “tangents.EXR”.

A parting note

You might –kind of randomly– ask, why does most of the binormals bake look bluish?
Good question!
Remember what the “binormal” is? It's the vector in 3D model space that aligns with the UP vector in the UV texture mapping. Well, it so happens that, as I mentioned in other tutorials, the CineMut/LaGrande standard is to have islands rotated such that UP in the texture aligns with Forward in model-space. If this standard were adhered to religiously during UV unwrap, the binormal bake would indeed look all the same color, namely the forward vector, positive z, or (0,0,1), –which with standard color encoding for vectors is (0.5, 0.5, 1.0) RGB, or sky-blue. In other words, it looks so bluish, in the case of this model, because I took pains to ensure that the UV islands are oriented in a particular way. If you don't adhere to such a standard, in your model's unwrap, the binormals bake will look pretty colorful.
So, then, why isn't the whole texture sky-blue?
Because the standard cannot always be adhered to. Front-facing and back-facing polygons simply don't have a texture direction that can be aligned to the forward direction; –not even roughly. Think about it…

(And why do we have such a standard? The main reason is that, during texturing, dirt smears and scratches are produced by using linear blurs fading downwards in the texture (i.e.: towards the back of the ship). If the UV islands were not standardized as per rotation, one would have to implement fades at different angles in different places… a nightmare. This standard allows us to treat all back-fading streaky things equally and process them globally. Another reason is to make the UV mapping more intuitive: If UP is known to be always the forward direction, it is much easier for a texturer to visually identify the correspondence of islands to mesh features, –by eliminating the rotational uncertainty.)

Now, for a sobering remark:
I'm just in the process of learning while hopefully standardizing the texturing pipelines, and I haven't got everything worked out yet. In my recent models, the binormals bake looked mostly golden, rather than blue, and at the time it seemed to me everything was alright, and as of this writing I can't figure out how I thought that was right…
Worse, I was just assuming that the forward direction in model space was positive Z, but I just checked it and it's positive Y, so I don't understand why I'm getting the results I am.
Worst, there are two 3D coordinate system standards: Blender uses the standard used by D3D; the Vegastrike engine uses the OpenGL standard, which is also used by the Wavefront .OBJ file format. The Blender exporter has an option to rotate the mesh on export, which I always use; but I've never figured out exactly what the correct rotation for a model is, in Blender, that results in the correct rotation of a model in .OBJ format. In Vegastrike, the forward vector for a ship is positive Z, and the UP vector (above the pilot's head) is positive Y.
Anyways, the PRT bakes are done in xNormal, which takes .OBJ as input, which is rotated by the exporter; whereas these normal, tangent and binormal bakes are made from within Blender, and therefore have no rotations.
I have no idea how all this plays together; my mind turns into a guacamole of confusion when facing these issues; so, consider this tutorial unfinished, for now. Sorry.

Also, this wiki doesn't have a Comments tab. If you can help, or have any comments or questions, please use the following forum thread:
http://wcjunction.com/phpBB2/viewtopic.php?p=21720


wc_info/modding/tan_binorm_bakes.txt · Last modified: 2009/11/06 01:08 by monkhouse