Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to calculate Tangent and Binormal?

Talking about bump mapping, specular highlight and these kind of things in OpenGL Shading Language (GLSL)

I have:

  • An array of vertices (e.g. {0.2,0.5,0.1, 0.2,0.4,0.5, ...})
  • An array of normals (e.g. {0.0,0.0,1.0, 0.0,1.0,0.0, ...})
  • The position of a point light in world space (e.g. {0.0,1.0,-5.0})
  • The position of the viewer in world space (e.g. {0.0,0.0,0.0}) (assume the viewer is in the center of the world)

Now, how can I calculate the Binormal and Tangent for each vertex? I mean, what is the formula to calculate the Binormals, what I have to use based on those informations? And about the tangent?

I'll construct the TBN Matrix anyway, so if you know a formula to construct the matrix directly based on those informations will be nice!

Oh, yeh, I have the texture coordinates too, if needed. And as I'm talking about GLSL, would be nice a per-vertex solution, I mean, one which doesn't need to access more than one vertex information at a time.

---- Update -----

I found this solution:

 vec3 tangent; vec3 binormal;  vec3 c1 = cross(a_normal, vec3(0.0, 0.0, 1.0)); vec3 c2 = cross(a_normal, vec3(0.0, 1.0, 0.0));  if (length(c1)>length(c2)) {     tangent = c1; } else {     tangent = c2; }  tangent = normalize(tangent);  binormal = cross(v_nglNormal, tangent); binormal = normalize(binormal); 

But I don't know if it is 100% correct.

like image 331
user464230 Avatar asked Mar 10 '11 05:03

user464230


People also ask

How do you find tangent and bitangent?

tangent points along the positive U texture coordinate axis for the face. To calculate the bitangent, we take the cross product of the normal and tangent vectors then multiply it by a constant in tangent. w which is the handedness of the tangent space.

What is tangent and binormal?

The plane defined by normal and binormal vectors is called the normal plane and the plane defined by binormal and tangent vectors is called the rectifying plane (see Fig. 2.6). As mentioned before, the plane defined by tangent and normal vectors is called the osculating plane.

How do you solve a binormal vector?

Explanation: To find the binormal vector, you must first find the unit tangent vector, then the unit normal vector. where is the vector and \displaystyle \left \| r(t)\right \| is the magnitude of the vector.


2 Answers

The relevant input data to your problem are the texture coordinates. Tangent and Binormal are vectors locally parallel to the object's surface. And in the case of normal mapping they're describing the local orientation of the normal texture.

So you have to calculate the direction (in the model's space) in which the texturing vectors point. Say you have a triangle ABC, with texture coordinates HKL. This gives us vectors:

D = B-A E = C-A  F = K-H G = L-H 

Now we want to express D and E in terms of tangent space T, U, i.e.

D = F.s * T + F.t * U E = G.s * T + G.t * U 

This is a system of linear equations with 6 unknowns and 6 equations, it can be written as

| D.x D.y D.z |   | F.s F.t | | T.x T.y T.z | |             | = |         | |             | | E.x E.y E.z |   | G.s G.t | | U.x U.y U.z | 

Inverting the FG matrix yields

| T.x T.y T.z |           1         |  G.t  -F.t | | D.x D.y D.z | |             | = ----------------- |            | |             | | U.x U.y U.z |   F.s G.t - F.t G.s | -G.s   F.s | | E.x E.y E.z | 

Together with the vertex normal T and U form a local space basis, called the tangent space, described by the matrix

| T.x U.x N.x | | T.y U.y N.y | | T.z U.z N.z | 

Transforming from tangent space into object space. To do lighting calculations one needs the inverse of this. With a little bit of exercise one finds:

T' = T - (N·T) N U' = U - (N·U) N - (T'·U) T' 

Normalizing the vectors T' and U', calling them tangent and binormal we obtain the matrix transforming from object into tangent space, where we do the lighting:

| T'.x T'.y T'.z | | U'.x U'.y U'.z | | N.x  N.y  N.z  | 

We store T' and U' them together with the vertex normal as a part of the model's geometry (as vertex attributes), so that we can use them in the shader for lighting calculations. I repeat: You don't determine tangent and binormal in the shader, you precompute them and store them as part of the model's geometry (just like normals).

(The notation between the vertical bars above are all matrices, never determinants, which normally use vertical bars instead of brackets in their notation.)

like image 87
datenwolf Avatar answered Sep 29 '22 11:09

datenwolf


Generally, you have 2 ways of generating the TBN matrix: off-line and on-line.

  • On-line = right in the fragment shader using derivative instructions. Those derivations give you a flat TBN basis for each point of a polygon. In order to get a smooth one we have to re-orthogonalize it based on a given (smooth) vertex normal. This procedure is even more heavy on GPU than initial TBN extraction.

    // compute derivations of the world position vec3 p_dx = dFdx(pw_i); vec3 p_dy = dFdy(pw_i); // compute derivations of the texture coordinate vec2 tc_dx = dFdx(tc_i); vec2 tc_dy = dFdy(tc_i); // compute initial tangent and bi-tangent vec3 t = normalize( tc_dy.y * p_dx - tc_dx.y * p_dy ); vec3 b = normalize( tc_dy.x * p_dx - tc_dx.x * p_dy ); // sign inversion // get new tangent from a given mesh normal vec3 n = normalize(n_obj_i); vec3 x = cross(n, t); t = cross(x, n); t = normalize(t); // get updated bi-tangent x = cross(b, n); b = cross(n, x); b = normalize(b); mat3 tbn = mat3(t, b, n); 
  • Off-line = prepare tangent as a vertex attribute. This is more difficult to get because it will not just add another vertex attrib but also will require to re-compose all other attributes. Moreover, it will not 100% give you a better performance as you'll get an additional cost of storing/passing/animating(!) vector3 vertex attribute.

The math is described in many places (google it), including the @datenwolf post.

The problem here is that 2 vertices may have the same normal and texture coordinate but different tangents. That means you can not just add a vertex attribute to a vertex, you'll need to split the vertex into 2 and specify different tangents for the clones.

The best way to get unique tangent (and other attribs) per vertex is to do it as early as possible = in the exporter. There on the stage of sorting pure vertices by attributes you'll just need to add the tangent vector to the sorting key.

As a radical solution to the problem consider using quaternions. A single quaternion (vec4) can successfully represent tangential space of a pre-defined handiness. It's easy to keep orthonormal (including passing to the fragment shader), store and extract normal if needed. More info on the KRI wiki.

like image 32
kvark Avatar answered Sep 29 '22 11:09

kvark