Advertisement Jump to content
Sign in to follow this  

OpenGL Tangent computation problem

This topic is 4907 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've followed Eric Lengyel's method exposed OpenGL thread and it works under certain texture coordinates. I observed that sometimes tangent vectors are not being computed correctly, as you can see in this image. The key is: normals(blue), tangents(red) and bitangents (green). Lighting in object is not from tangent space, is object space per-pixel lighting (the one i want to improve with tangent space when it works). I don't know what's up with tangents, so if you can give some light on it, let me know. Thanks.

Share this post

Link to post
Share on other sites
Oh, man...
You know, Eric's method computes TS only per-triangle - all the real problems begin just after that. Converting per-triangle information to per-vertex one is the real killer for all TS computators. Just averaging don't work in a fraction of cases, and it matters - where you have mirrored and not-mirrored triangles meeting in adjacent vertex and so on.
Try NVMeshMender library - it gives somewhat correct result, if your meshes aren't completely crazy of course...
Or one method, is to collect all triangle TS-s in a vertex, then invert mirrored ones, then average and then split the vertex in two - for normal and for mirrored triangles. Then just copy the averaged TS in the normal vertex, then in averaged one and invert it back.

Share this post

Link to post
Share on other sites
Here's my implementation (rather the same as Eric's):

void Mesh::ComputeTangentSpaceMatrices ( )
Vector3 *pkSTangents;
pkSTangents = new Vector3[m_uiNumVertices*2];
memset( pkSTangents, 0, sizeof(Vector3)*m_uiNumVertices*2);

Vector3 *pkTTangents = &pkSTangents[m_uiNumVertices];

m_pkTangents = new Vector3[m_uiNumVertices];
m_pkBitangents = new Vector3[m_uiNumVertices];

// sustituir el 0 por el canal que corresponda al normalmap
Vector2 *pkTexCoords = (Vector2 *)m_apkTexCoords[0];

if ( !pkTexCoords )

for ( unsigned int uiT=0; uiT<m_uiNumFaces; uiT++ )
unsigned int uiI1 = m_pusIndices[uiT + 0];
unsigned int uiI2 = m_pusIndices[uiT + 1];
unsigned int uiI3 = m_pusIndices[uiT + 2];

Vector3 &rkV1 = m_pkVertices[uiI1];
Vector3 &rkV2 = m_pkVertices[uiI2];
Vector3 &rkV3 = m_pkVertices[uiI3];
Vector2 &rkT1 = pkTexCoords[uiI1];
Vector2 &rkT2 = pkTexCoords[uiI2];
Vector2 &rkT3 = pkTexCoords[uiI3];

Vector3 kEdge1 = rkV2 - rkV1;
Vector3 kEdge2 = rkV3 - rkV1;
Vector2 kTex1 = rkT2 - rkT1;
Vector2 kTex2 = rkT3 - rkT1;

float fInvDet = 1.0f/(kTex1.x * kTex2.y - kTex1.y * kTex2.x );
Vector3 kSDir( (kTex2.y * kEdge1.x - kTex1.y * kEdge2.x) * fInvDet,
(kTex2.y * kEdge1.y - kTex1.y * kEdge2.y) * fInvDet,
(kTex2.y * kEdge1.z - kTex1.y * kEdge2.z) * fInvDet );
Vector3 kTDir( (kTex1.x * kEdge2.x - kTex2.x * kEdge1.x) * fInvDet,
(kTex1.x * kEdge2.y - kTex2.x * kEdge1.y) * fInvDet,
(kTex1.x * kEdge2.z - kTex2.x * kEdge1.z) * fInvDet );

pkSTangents[uiI1] += kSDir;
pkSTangents[uiI2] += kSDir;
pkSTangents[uiI3] += kSDir;

pkTTangents[uiI1] += kTDir;
pkTTangents[uiI2] += kTDir;
pkTTangents[uiI3] += kTDir;

for ( unsigned int uiV=0; uiV<m_uiNumVertices; uiV++ )
Vector3 &rkNormal = m_pkNormals[uiV];
Vector3 &rkAuxTangent = pkSTangents[uiV];
float fHandedness = ((rkNormal ^ rkAuxTangent)*pkTTangents[uiV] < 0 ? -1.0f : 1.0f);

Vector3 kTangent = rkAuxTangent - rkNormal * (rkNormal*rkAuxTangent);

Vector3 kBitangent = (rkNormal ^ kTangent) * fHandedness;

m_pkTangents[uiV] = kTangent;
m_pkBitangents[uiV] = kBitangent;

delete [] pkSTangents;


I think it's correct because it works if UV's are good enough, but there are meshes that cannot get that UV (unfolding, many parts, ...). I can see Eric's method computes the tangents per triangle, but then it adds up to that temporary S and T tangents array to get the average later. After that, it computes tangent/bitangent having into account handedness.

I debugged the code with a simple cube with smoothed faces (averaged normals) and noticed that for 3 of 12 triangles the inverse determinant is huge (determinant near zero).

Share this post

Link to post
Share on other sites
Original post by YengaMatiC
Here's my implementation (rather the same as Eric's):

It don't handle the case when mirrored and non-mirrored triangles meet at a vertex.

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using, you agree to our community Guidelines, Terms of Use, and Privacy Policy. is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!