Correct Per-Vertex Tangent & Binormals

What’s the correct way to do per-vertex tangent generation?

I’ve seen a few people do it like this:
C1 = cross( N, {0.0, 0.0, 1.0} )
C2 = cross( N, {0.0, 1.0, 0.0} )

T = (||C1|| > ||C2||) ? C1 : C2
B = cross(N, T)
Where N is a vertex normal

I’ve also seen it done like this:
E2-1 = V2 - V1
E3-1 = V3 - V1

T = E2-1.xyz / E3-1.u
B = cross(N, T)
Where V is a vertex containing a position (XYZ) and a correct texture mapping coordinate (UV)

Also, if the normal is transformed by an inverse transpose of the object matrix, what would the tangents be transformed by?

Thanks in advance!

The “correct” way is this^.

Why? If I remember correctly it accounts for normal mapping and less visible seams on all of your textures. There was a thread on this a couple months ago that has more insight. Here’s a good resource:

http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/#Computing_the_tangents_and_bitangents

The thread I’m talking about:
http://www.java-gaming.org/topics/reopened-calculating-normal-vectors/33838/view.html

Does it matter? Not in my experience, but technically I don’t use normal mapping just bump mapping so I’m sure there is merit to doing it the “correct” way.

I assume the same inverse transpose matrix, because you’d want the tangent and bitangent/binormal to retain the perpendicular qualities, but my math isn’t good here.

heres a simple sniplet : tested with CCW triangles. just flip order for CW.

public static vec3 getTangent ( vec3 v0,  vec3 v1,  vec3 v2, // three vertices of a triangle/face
                                vec2 st0, vec2 st1, vec2 st2 ) // texture-coords for these vertices
{
  float x1   = v1.x - v0.x; 
  float x2   = v2.x - v0.x;
  float y1   = v1.y - v0.y;
  float y2   = v2.y - v0.y;
  float z1   = v1.z - v0.z;
  float z2   = v2.z - v0.z;

  float s1   = st1.x - st0.x;
  float s2   = st2.x - st0.x;
  float t1   = st1.y - st0.y;
  float t2   = st2.y - st0.y;

  float r    = 1.0f / ( s1 * t2 - s2 * t1 );

  vec3 tangent = new vec3 (( t2 * x1 - t1 * x2 ) * r, ( t2 * y1 - t1 * y2 ) * r, ( t2 * z1 - t1 * z2 ) * r );

  // vec3 bitangent = new vec3 ((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); // compute this in a shader instead

  return normalize(tangent);
}

vertex-shader could do something like …

#version 140

in vec3 tangent; // incoming tangent as computed above

[...]

out mat3 tbn; // fragment shader input

void main()
{
  vec3 normal = gl_NormalMatrix * gl_Normal;

  vec3 T = normalize ( gl_NormalMatrix * tangent );
  vec3 B = normalize ( cross ( T, normal ) );

  tbn = mat3( T, B, normal );

  [...]
}


in the fragment shader somthing like

vec3 normal = normalize(tbn * vec3(0.0,0.0,1.0))

should work.

disclaimer : there is at least one bug in that code! :wink:

Thanks for clearing that up! +1