# Something's wrong with my tangents and bitangents.

## Recommended Posts

Hulag    134
It seems I'm doing something wrong when I calculate the tangents and bitangents of my mesh. I really need some help with this since I'm trying to do bumpmapping and specular reflections and they look all wrong. Here I have the function I use to calculate the tangents and bitangents. BTW, I did read [url]http://www.terathon.com/code/tangent.html[/url] but it doesn't calculate bitangents, and also the code is a bit wierd to me because the structures of the geometry are different so I don't really know what I'm supposed to do. Here is my code:
void MathFuncs::calculateTangent(Mesh *CurrentMesh)
{
for (int i = 0; i < (intCurrentMesh->meshLayers.size(); ++i)
{
for(int j = 0; j < CurrentMesh->meshLayers[i]->numLayerGroups; ++j)
{
for(int k = 0; k < (CurrentMesh->meshLayers[i]->layerMaterialIndices[j]->numIndices / 3); ++k)
{
Mesh::MeshVert *v0 = &CurrentMesh->meshLayers[i]->layerVertices[j + (k * 3)];
Mesh::MeshVert *v1 = &CurrentMesh->meshLayers[i]->layerVertices[j + (k * 3 + 1)];
Mesh::MeshVert *v2 = &CurrentMesh->meshLayers[i]->layerVertices[j + (k * 3 + 2)];

vec3 normal, tangent, biTangent;
vec3 e0 = vec3(0.0f, v1->UV.x - v0->UV.x, v1->UV.y - v0->UV.y);
vec3 e1 = vec3(0.0f, v2->UV.x - v0->UV.x, v2->UV.y - v0->UV.y);

for(int l = 0; l < 3; l++)
{
e0.x = v1->position[l] - v0->position[l];
e1.x = v2->position[l] - v0->position[l];

vec3 v;
v.cross(e0, e1);

tangent[l] = -v[1] / v[0];
biTangent[l] = -v[2] / v[0];
}

tangent.normalize();
biTangent.normalize();
normal.cross(tangent, biTangent);
normal.normalize();

v0->biTangent.cross(v0->normal, tangent);
v0->biTangent.normalize();
v0->tangent.cross(v0->biTangent, v0->normal);
if(normal * v0->normal < 0.0f)
v0->biTangent = -v0->biTangent;

v1->biTangent.cross(v1->normal, tangent);
v1->biTangent.normalize();
v1->tangent.cross(v1->biTangent, v1->normal);
if(normal * v1->normal < 0.0f)
v1->biTangent = -v1->biTangent;

v2->biTangent.cross(v2->normal, tangent);
v2->biTangent.normalize();
v2->tangent.cross(v2->biTangent, v2->normal);
if(normal * v2->normal < 0.0f)
v2->biTangent = -v2->biTangent;
}
}
}
}


Just so it isn't so hard to understand the code, a Mesh is composed of several meshLayers, inside each meshLayers there are LayerGroups, each group has a number of lists, there is one list per material in the layer. The indices stored are actually indices to the vertex array in the meshLayer. So for example I have two triangles with different materials in a single layer, there would be just two layerMaterialIndices with 3 indices each, and 6 vertices in layerVertices in the layer. I hope you people can help me cause I really need to solve this. [Edited by - Hulag on June 3, 2005 12:48:23 PM]

##### Share on other sites
Guest Anonymous Poster
Screen shot?
Try building it incrementally - (bump) no color vertex, bump w/ color vertex, bump w/color vertext & specular highlights.

Why does it look wrong?

##### Share on other sites
Guest Anonymous Poster
This is C++ and directx 9?

##### Share on other sites
Hulag    134
Quote:
 Original post by Anonymous PosterScreen shot?Try building it incrementally - (bump) no color vertex, bump w/ color vertex, bump w/color vertext & specular highlights.Why does it look wrong?

Yes, I have done that testing, in fact if I draw just the diffuse component (which is just the bumpmapping) it doesn't look that wrong, there is something wierd but it doesn't look completly wrong with many normal maps, but one simple test that should come out right if the binormal and tangents are right is when I have a normal map that's completly flat, if they were right then everything should look some kind of gray, but everything looks black.

Quote:
 Original post by Anonymous PosterThis is C++ and directx 9?

C++ and OpenGL.

Thanks.

##### Share on other sites
Hulag    134
I have another example of why I think the tangent and bitangents are wrong, I make a 100x100x100 box where all the faces face to the inside of the box, I put that box in such way that the coords 0.0f, 0.0f, 0.0f are the exact center of the box. Now I put a light at 0.0f, 0.0f, 0.0f and the camera at 0.0f, 0.0f, 0.0f. Now if everything is correct, all the faces should look the same but here is an screenshot:

##### Share on other sites
ganchmaster    134
Bitangent? Cotangent? I think you mean binormal.

Bitangent and cotangent mean something else:

http://mathworld.wolfram.com/Bitangent.html
http://mathworld.wolfram.com/Cotangent.html

This is probably the source of your problems :).

##### Share on other sites
Hulag    134
Quote:
 Original post by ganchmasterBitangent? Cotangent? I think you mean binormal. Bitangent and cotangent mean something else:http://mathworld.wolfram.com/Bitangent.htmlhttp://mathworld.wolfram.com/Cotangent.html This is probably the source of your problems :).

If you read http://mathworld.wolfram.com/BinormalVector.html you will see that binormal isn't right either.
Here is a quote from http://www.terathon.com/code/tangent.html
"The term binormal is commonly used as the name of the second tangent direction (that is perpendicular to the surface normal and s-aligned tangent direction). This is a misnomer. The term binormal pops up in the study of curves and completes what is known as a Frenet frame about a particular point on a curve. Curves have a single tangent direction and two orthogonal normal directions, hence the terms normal and binormal. When discussing a coordinate frame at a point on a surface, there is one normal direction and two tangent directions, which should be called the tangent and bitangent. Somewhere along the line, somebody used the wrong term and it caught on. Please help remedy the botched terminology by using the word bitangent instead of binormal in the context of bump mapping, etc."

##### Share on other sites
ganchmaster    134
That guy is delusional if he thinks that he is going to change the terminology used everywhere for normal mapping. All he is going to do is introduce confusion for those people unlucky enough to deal with his writing.

You can call it whatever you want, but if I were you I'd do yourself a favor and not bother with the "crusade".

Anyway, Binormal is still a better name than bitangent (or cotangent, which you also used for some reason). It means about the same thing for surfaces as it does for curves - B = T x N. So computationally it's the same thing and no-one who looks it up in Mathworld or elsewhere will be confused. Bitangent is a valid term for something else and shouldn't be hijacked just because this guy wants to split hairs about the difference between curves and surfaces. Yeah, the binormal isn't really normal to the surface...so what? Everyone knows what it means.

##### Share on other sites
Hulag    134
Quote:
 Original post by ganchmasterThat guy is delusional if he thinks that he is going to change the terminology used everywhere for normal mapping. All he is going to do is introduce confusion for those people unlucky enough to deal with his writing. You can call it whatever you want, but if I were you I'd do yourself a favor and not bother with the "crusade". Anyway, Binormal is still a better name than bitangent (or cotangent, which you also used for some reason). It means about the same thing for surfaces as it does for curves - B = T x N. So computationally it's the same thing and no-one who looks it up in Mathworld or elsewhere will be confused. Bitangent is a valid term for something else and shouldn't be hijacked just because this guy wants to split hairs about the difference between curves and surfaces. Yeah, the binormal isn't really normal to the surface...so what? Everyone knows what it means.

I'm just going to say two things. First, this thread wasn't made to discuss a bunch of terms, I just need help to solve a big problem. And second, just because all the people use a term to describe something, doesn't mean its the right term, but it is true that most people understand what it means.

PS: I confused bitangents and cotangents in my head for a second, never meant to actually write "cotangent" in my post.

##### Share on other sites
Guest Anonymous Poster
Quote:
 Original post by ganchmasterThat guy is delusional if he thinks that he is going to change the terminology used everywhere for normal mapping. All he is going to do is introduce confusion for those people unlucky enough to deal with his writing. You can call it whatever you want, but if I were you I'd do yourself a favor and not bother with the "crusade". Anyway, Binormal is still a better name than bitangent (or cotangent, which you also used for some reason). It means about the same thing for surfaces as it does for curves - B = T x N. So computationally it's the same thing and no-one who looks it up in Mathworld or elsewhere will be confused. Bitangent is a valid term for something else and shouldn't be hijacked just because this guy wants to split hairs about the difference between curves and surfaces. Yeah, the binormal isn't really normal to the surface...so what? Everyone knows what it means.

that attitude might imply that it would be fair to call people niggers, chinks and wogs. but thats an unrelated matter.

##### Share on other sites
SimmerD    1210

1) What if v[ 0 ] == zero?

2) Why are your orthonormalizing the vectors? This is not needed in general, and can produce wrong results in many cases.

3) Why are you calculating the normal from the t x b? You should just use the normal that came in or compute it directly. This helps with mirrored texture coordinates.

Have you looked at nvMeshMender?

http://developer.nvidia.com/object/NVMeshMender.html

Not as fast as it could be, but it works pretty well, IMHO.

##### Share on other sites
Quote:
 Original post by ganchmasterYeah, the binormal isn't really normal to the surface...so what? Everyone knows what it means.

Clearly it is news to you, but terminology matters. For example, a 'line' is an entity with infinite extent. A 'line segment' is a finite section of a line. If you, for example, ask for how to find the closest points between two lines when you mean two line segments you're gonna get an answer completely irrelevant to your problem.

The same applies to 'binormal' and 'bitangent' and Eric's "crusade" is justified. FYI, most if not all game and graphics professionals I know have corrected their usage of these terms as it is obvious that Eric is right. It says a lot that they find it important to do so. It also says a lot that you don't.

##### Share on other sites
ganchmaster    134
Quote:
Original post by Christer Ericson
Quote:
 Original post by ganchmasterYeah, the binormal isn't really normal to the surface...so what? Everyone knows what it means.

Clearly it is news to you, but terminology matters. For example, a 'line' is an entity with infinite extent. A 'line segment' is a finite section of a line. If you, for example, ask for how to find the closest points between two lines when you mean two line segments you're gonna get an answer completely irrelevant to your problem.

The same applies to 'binormal' and 'bitangent' and Eric's "crusade" is justified. FYI, most if not all game and graphics professionals I know have corrected their usage of these terms as it is obvious that Eric is right. It says a lot that they find it important to do so. It also says a lot that you don't.

Yeah, I totally agree that terminology matters. I think people should use terminology that is not confusing. Despite your comments about "most if not all" graphics professionals, "Binormal" is heavily enshrined in the literature. And it's confusing to try to introduce a new term.

it is hardcoded in D3D for instance:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/graphics/reference/hlslreference/hlslreference.asp

ATI uses the term

GPU Gems mentions it:

http://www.awprofessional.com/content/images/0321228324/samplechapter/fernando_ch03.pdf

Dave Eberly, who I am sure you are familiar with, uses the term:

http://www.geometrictools.com/Foundation/Effects/Wm3BumpMapEffect.cpp

The granny SDK which generates tangent space vectors uses it:

A tutorial on gamedev itself uses it

http://nehe.gamedev.net/data/articles/article.asp?article=20

I could go on but I think I make my point. You are basically setting up a straw man by saying "terminology matters" and then knocking it over. I never said terminology didn't matter. I think people should use terminology that is not confusing. It's confusing when people decide to adopt a new terminology to replace a perfectly good one that is widely used. Further the term "bitangent" already means something else, so how is it an improvement to use that? And the term binormal is appropriate since for either curves or surfaces, B = T x N.

It's really kind of ironic that people are attempting to put a self-righteous smackdown on me for this, since I am actually kind of a stickler for calling things by their right name - more than most people I would say. So yes I say line when I mean line and line segment when I mean that, and so on. I think you would find your comment about how it "says a lot that I don't" humorous if you knew me. Maybe it's better to not get personal.

I don't know if I should dignify the anonymous post with a response, but the analogy is obviously flawed, since it is not harmful to anyone to call something a binormal :). Probably that post should be deleted anyway, especially since it was an Anonymous Coward (tm).

##### Share on other sites
kusma    170
terminology is still damn irrelevant to this thread.

##### Share on other sites
Hulag    134
Ok, first an update. I had the normals of the face wrong since I was sharing them among faces that shared a vertex even if they didn't share the same normal. So now that's solved but I still have problems where the bumpiness of the faces point kinda to the other side so I will have to check out what's wrong with that but I'm not so sure. In an hour or so I will have a few screenshots.

Quote:
 Original post by SimmerDA couple of comments.1) What if v[ 0 ] == zero?

True, I added the following check if(fabs(v[0]) > EPSILON) to handle that situation.

Quote:
 Original post by SimmerD2)Why are your orthonormalizing the vectors? This is not needed in general, and can produce wrong results in many cases.

I didn't know that, nn which case would that be wrong?

Quote:
 Original post by SimmerD3) Why are you calculating the normal from the t x b? You should just use the normal that came in or compute it directly. This helps with mirrored texture coordinates.

The normals used were actually from the mesh that I loaded. The normal defined with t x b was just for for the final check fo the values but I got rid of it since it did no good.

Quote:
 Original post by SimmerDHave you looked at nvMeshMender?http://developer.nvidia.com/object/NVMeshMender.htmlNot as fast as it could be, but it works pretty well, IMHO.

I checked it out but it does way too much, I just need to get correct bitangents and tangents.

Thanks for the help SimmerD.

##### Share on other sites
SimmerD    1210
In most cases orthonormalizing is the ok to do. In the case of a non-square texture, or one mapped in a non-square manner, you don't necessarily want your tangents & binormals to be the same scale, for instance.

Doug Rogers has shown mathematically that for proper local lighting, the tangent space basis is not a rotation matrix, and should deform per-pixel in order to get the right thing. So, you shold not normalize these vectors per-pixel, for instance. Sorry, I don't have a link.

##### Share on other sites
Hulag    134
Quote:
 Original post by SimmerDIn most cases orthonormalizing is the ok to do. In the case of a non-square texture, or one mapped in a non-square manner, you don't necessarily want your tangents & binormals to be the same scale, for instance.Doug Rogers has shown mathematically that for proper local lighting, the tangent space basis is not a rotation matrix, and should deform per-pixel in order to get the right thing. So, you shold not normalize these vectors per-pixel, for instance. Sorry, I don't have a link.

Cool, thanks for letting me know. And don't worry, I know how to use Google :)

##### Share on other sites
Eric Lengyel    3461
Hi Hulag --

The code at http://www.terathon.com/code/tangent.html does exactly what you need. The arrays pointed to by tan1 and tan2 contain the tangents and bitangents, respectively, after the main loop. The these are used to calculate a handedness factor, and then the bitangents are thrown out because you're expected to recalculate them in the vertex program using B = (N x T) * T.w.

Unfortunately, the example which is susceptible to division by zero has been around for a few years and copied all over the place, but it is just plain wrong. The algorithm is obviously not robust, even with that hacky epsilon test. Because you're dividing by the difference in vertex coordinates, it can't even handle an axis-aligned cube correctly.

-- Eric Lengyel

##### Share on other sites
Eric Lengyel    3461
Quote:
 Original post by ganchmasterBinormal is still a better name than bitangent. It means about the same thing for surfaces as it does for curves - B = T x N.

That's not true. For a curve, yes, it's always the case that B = T x N. The vectors are ordered so that the local Frenet frame's x, y, and z axes are aligned to the directions given by T, N, and B, in that order. In tangent space, however, the order is different: the x, y, and z axes correspond to T, B, and N. If your texture coordinates are right-handed (so that your texture isn't flipped), then B = N x T, which is different than that for curves. If your texture is flipped, then you do get B = -N x T = T x N. The bitangent vector is defined in a completely different way and has a completely different meaning for surfaces than does the binormal vector for curves.

The confusion arises when people new to the subject encounter the term binormal. Sure, for curves it makes perfect sense since there is one tangent direction and two orthogonal normal directions whose names both contain the word "normal". For surfaces, there's one normal direction and two orthogonal tangent directions, and it's appropriate for the two tangent directions to both contain the word "tangent". Just because a less-than-optimal term has been used for a while doesn't mean that we should keep it that way. I think the best thing that authors can do now is explain that binormal and bitangent are used interchangeably, but also explain that the usage of binormal is not technically correct.

BTW, the entry for bitangent on MathWorld does state that it's incomplete. Many mathematical terms have multiple meanings (just look at "normal"), and using the term "bitangent vector" would not be hijacking anything from the term "bitangent", which is a line that is tangent to a curve at two points. Myself and others have recently contacted Wolfram about updating MathWorld with the meaning of "bitangent vector" in the context of bump mapping, so hopefully we'll see it there soon.

##### Share on other sites
Eric Lengyel    3461
Oh yeah, don't call me delusional. You might make me angry. And you wouldn't like me angry.

##### Share on other sites
ganchmaster    134
Quote:
 Original post by Eric LengyelIf your texture coordinates are right-handed (so that your texture isn't flipped), then B = N x T, which is different than that for curves.

Your discussion was much more interesting than Christer's and you make a good case. You're right, of course, the binormal is the Y of the coordinate basis, which can be either left or right handed, but B = N x T is probably better. I think the analogy to the curve is perfectly good though. Saying that the the binormal is the other element of a coordinate basis is a good way of describing it for either curves of surfaces.

Quote:
 The confusion arises when people new to the subject encounter the term binormal.

I don't remember being confused about this point when I learned about bump mapping. It happened that I knew the term from curves and I immediately understand what was going on. But I hardly imagine that either people who are or aren't familiar with curves freak out and are totally incapacitated by the fact that the word "normal" is inside the word "binormal", even though it is not normal to the surface, and it points the other way. Have you really seen a problem with this?

Still I understand your point and if we had to pick a new term today, tabula rasa, I'd probably pick bitangent over binormal. I also think you will introduce more confusion for a new student by introducing it at this point. I think that the danger of confusion from two terms that mean the same thing is worse than the danger of confusion from an somewhat inconsistent but still meaningful usage. And who is to say bitangent will ever be more than a minority contender? All of Christer's friends, apparently, and none of mine :).

I probably shouldn't have said "delusional"; "mistaken" would have been adequate. In any case, time will tell. And,

Quote:
 Oh yeah, don't call me delusional. You might make me angry. And you wouldn't like me angry.

Hmmm...I'm not too concerned.

##### Share on other sites
Quote:
 Original post by ganchmasterYour discussion was much more interesting than Christer's and you make a good case.

I wasn't trying to be interesting. I was just implying that your post was arrogant (calling Eric delusional) and not very well informed (saying that binormal "means about the same thing for surfaces as it does for curves" when in fact it means something quite orthogonal!).

As for being interesting and making a case... well...

For a point Q on a surface S, there is a single surface normal vector (or just normal) N. However, there's an infinite number of tangent vectors at Q, forming a tangent plane PT.

If you want to establish a tangent space coordinate system at Q, you have to pick three vectors: N, and two (orthogonal) tangent vectors, T1 and T2, from the tangent plane PT.

The "crusade" is that it does not make sense to call this pair of tangent vectors "tangent" and "binormal" because a binormal would be expected to be normal to the surface but is here tangential to it. It does, however, make perfect sense to call the two tangent vectors "tangent" and "bitangent".

As Eric already explained, the term "binormal" originates in the establishing of a coordinate system at a point on a curve (a Frenet frame). In complete opposite of points of a surface, a point on a curve has just a single tangent vector (defined 'along' the curve) but an infinite number of normals, forming the normal plane. (If you look up "normal plane" in MathWorld you'll see it refers to just curves, _not_ surfaces, defining the normal plane to be the plane spanned by the normal and the binormal.)

--

That "bitangent" has a different meaning already shouldn't stop us from universally adopting the term in favor of "binormal" for two reasons.

First, we use similar or same terms to mean different things all the time. Are you aware that the term "tangent space" that we use in graphics differs from the term "tangent space" used by a topologist? In that mathematical topology predates computer graphics, we in CG clearly overloaded the term for our uses. We can do it again for "bitangent"!

Second, in CG when we say "bitangent" we are really talking about the "bitangent vector" (i.e. it's a shorthand). The existing "bitangent" term isn't with regards to a vector at all but to a line. It also isn't used in the context of a tangent space. Thus there is no risk for confusion.

As Eric said, the right thing to do is for authors to use "bitangent" but note that it is frequently called binormal even though that is not technically correct.

##### Share on other sites
christian h    200
Back to business from this silly little argument ;)

What are you using to create the bumpmaps? Or are they pre-made?
nVidia's bumpmap-generator that takes a heightmap as input and outputs normal-map has the y-axis "upside-down", cause it works in texture-space where y increases downwards, when in 3d y increases upwards (or to the right, depending on the coordinate system..).

nVidia's code has also slipped to some other bumpmap-generators, like the normalmap-plugin for GIMP.
Then again, ATI uses totally different system on their own..

So try swapping the normalmap pixels/components with "x = 255 - x".
And for debugging, render your normals and tangents and bi*s, to see what way they are actually pointing to..

ch.

##### Share on other sites
Dave Eberly    1173
Someone alerted me to this thread about the terminology "binormal" versus "bitangent". As a mathematician, I have the final say. The term "binormal" is always used for curves and is introduced in the discussion of the Frenet frame (for curves). The term "bitangent" is always used for surfaces and is introduced in the discussion of the Darboux frame (for surfaces). The use of "binormal" for bump maps is incorrect terminology. It should be "bitangent". Yes, my code used "binormal" because I was trying to be consistent with what graphics folks use. My error. I have just changed my source code to use "bitangent" and will repost is shortly.

Note that confusion between the two will arise in the special case of analyzing a curve that happens to lie on a surface. In general, the (curve) binormal will not match the (surface) bitangent at a point.

##### Share on other sites
reltham    642
Can someone get Microsoft to change DirectX? It has Binormal in the API in several places. What about all the ATI and nVidia stuff? The GL extensions?

Having all the new books calling it bitangent while all the code actually uses binormal all over the place because of the APIs would be messy and confusing to new folks.

Also, if it's okay to overload bitangent's meaning, why isn't okay to overload binormal's meaning? Wouldn't that be easier since it's already established?

I honestly don't care what you call it, I just want to properly calulate it and use it to make stuff look spiffy on screen.